From 106f1e30bfaa650546b7c4a903752a9398f0ffd3 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" <39563805+anthony-santana@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:37:13 -0500 Subject: [PATCH 001/311] Merge in dynamics work and squash commit history (#12) * add skeleton of new API functions without connecting to docs yet Signed-off-by: A.M. Santana * updates Signed-off-by: A.M. Santana * boilerplate Signed-off-by: A.M. Santana * clean up linking bug Signed-off-by: A.M. Santana * adding schedule implementation * fixing typo * small updates Signed-off-by: A.M. Santana * Cleaning up docs preview for PR #6. * push initial tests that show memory leak in current translation to complex matrix Signed-off-by: A.M. Santana * completely work around eigen in default elementary ops Signed-off-by: A.M. Santana * storing changes Signed-off-by: A.M. Santana * start to build out callback function class Signed-off-by: A.M. Santana * working function wrapper implementation Signed-off-by: A.M. Santana * implement complex matrix equality operator Signed-off-by: A.M. Santana * push with broken to matrix overload Signed-off-by: A.M. Santana * fix scoping issue found in to matrix Signed-off-by: A.M. Santana * fill out unit tests for to matrix overload Signed-off-by: A.M. Santana * add skeleton of new API functions without connecting to docs yet Signed-off-by: A.M. Santana * updates Signed-off-by: A.M. Santana * boilerplate Signed-off-by: A.M. Santana * clean up linking bug Signed-off-by: A.M. Santana * initial scalar value support and tests Signed-off-by: A.M. Santana * update to_value to evaluate to amtch python api Signed-off-by: A.M. Santana * adding kwargs capability in C++ by using std::variant and std::bind * renaming VariantArg to NumericType to match Python * adding a std::variant returntype nad adjusting the test accordingly * Cleaning up docs preview for PR #7. * commit first draft of arithmetic against complex doubles Signed-off-by: A.M. Santana * little build errors Signed-off-by: A.M. Santana * call generator directly instead of evaluate in operator overloads Signed-off-by: A.M. Santana * remove old constructor Signed-off-by: A.M. Santana * copy constructor Signed-off-by: A.M. Santana * push partially working arithmetic operations Signed-off-by: A.M. Santana * comment back in tests Signed-off-by: A.M. Santana * remove constructor that takes removed parameters member Signed-off-by: A.M. Santana * still having memory issues Signed-off-by: A.M. Santana * implement and test remaining pre defined elementary ops except displace and squeeze. implement complex matrix exponential Signed-off-by: A.M. Santana * fix for segfault in copy constructor but still have memory issues from arithmetic Signed-off-by: A.M. Santana * temp patch to get scalar arithmetic against complex doubles working. against doubles wrapped in functions still broken Signed-off-by: A.M. Santana * potential fix for callback function going out of scope Signed-off-by: A.M. Santana * confirm fix for scalar ops from functions and reenable tests Signed-off-by: A.M. Santana * begin to support scalar against scalar ops and simplify other arithmetic definitions with macros Signed-off-by: A.M. Santana * support for remaining arithmetic against other scalar ops except for assignment operators Signed-off-by: A.M. Santana * add checks to ensure local variables are picked up fine in generator functions Signed-off-by: A.M. Santana * clean up test file Signed-off-by: A.M. Santana * full refactor to take a [arameter map with elementary operator implementation back under construction Signed-off-by: A.M. Santana * fix elementary ops Signed-off-by: A.M. Santana * start to manually merge in operator sum changes Signed-off-by: A.M. Santana * camel case some things and underscore some others Signed-off-by: A.M. Santana * fix build errors from header file Signed-off-by: A.M. Santana * refactor implementation file names, delete unused dynamics.h, refactor unittests with new dynamics folder Signed-off-by: A.M. Santana * storing large set of changes to dynamics folder structure and implementing more arithmetic Signed-off-by: A.M. Santana * add cudaq tensor type Signed-off-by: Alex McCaskey * add uint8 tensor to python api Signed-off-by: Alex McCaskey * Update unittests/utils/tensor_tester.cpp Signed-off-by: Eric Schweitz * Update unittests/utils/tensor_tester.cpp Signed-off-by: Eric Schweitz * Update unittests/utils/tensor_tester.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update python/runtime/utils/py_tensor.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/extension_point.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/extension_point.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/tensor.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/tensor.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/tensor_impl.h Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/tensor_impl.h Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/tensor_impl.h Signed-off-by: Eric Schweitz * Spelling fixes. Signed-off-by: Eric Schweitz * push current state Signed-off-by: A.M. Santana * Format Signed-off-by: Anna Gringauze * Format Signed-off-by: Anna Gringauze * Drop debug code. Signed-off-by: Eric Schweitz * Cleanup tensor types a tad. Signed-off-by: Eric Schweitz * Fix spelling Signed-off-by: Anna Gringauze * Fix spelling and format python code Signed-off-by: Anna Gringauze * Sort spelling allowlist Signed-off-by: Anna Gringauze * Cleaning up docs preview for PR #9. * more arithmetic support Signed-off-by: A.M. Santana * Update spelling and formatting Signed-off-by: Anna Gringauze * more implementation and more tests Signed-off-by: A.M. Santana * Update spelling and formatting Signed-off-by: Anna Gringauze * This is an attempt to sort out the different ownership models of tensor data. The python stub for "take" needs to be implemented however. The semantics are: - copy : the tensor object makes a copy of the data and owns the copy. i.e. there is a unique_ptr. - take : the tensor object steals a copy of the data from the caller. In order for this case to make sense, we want the caller to guarantee that the data is unique before we steal it. This can be done by forcing the client code to wrap the tensor data in a unique_ptr *before* the take call. - borrow : In this case, the tensor object has no ownership of the tensor data and just naively assumes the client will manage the data correctly. In this case, a raw pointer to the client's data is used. Signed-off-by: Eric Schweitz * Update the missing py_tensor code. Signed-off-by: Eric Schweitz * Add handling for empty shape case. Signed-off-by: Eric Schweitz * Add more python tests Signed-off-by: Anna Gringauze * Add a take() with move semantics. Signed-off-by: Eric Schweitz * fix product operator constructor issue and tests Signed-off-by: A.M. Santana * fix more test more Signed-off-by: A.M. Santana * cover all arithmetic Signed-off-by: A.M. Santana * update before merging in tensor pr Signed-off-by: A.M. Santana * Fix __init__ argument order so tests pass. The shape must come first. Signed-off-by: Eric Schweitz * Remove StateTensor. (Not used.) Signed-off-by: Eric Schweitz * Add more comments on how to use this stuff. Signed-off-by: Eric Schweitz * Added python tests and fixed some issues * Merge with tensor Signed-off-by: Anna Gringauze * DCO Remediation Commit for Anna Gringauze I, Anna Gringauze , hereby add my Signed-off-by to this commit: e78def4e1370e18f497ca891884d69e9f4421f3b Signed-off-by: Anna Gringauze * Add some boilerplate for tensor operators. Signed-off-by: Eric Schweitz * Make the compiler work a bit harder. Signed-off-by: Eric Schweitz * Add move constructor. Signed-off-by: Eric Schweitz * Make sure the return values for operators work as expected. Signed-off-by: Eric Schweitz * clang-format Signed-off-by: Eric Schweitz * Add override to dtor. Signed-off-by: Eric Schweitz * Add forward decls. Signed-off-by: Eric Schweitz * More fussy templates. Signed-off-by: Eric Schweitz * Workaround warnings from g++. Signed-off-by: Eric Schweitz * Fix typos. Signed-off-by: Eric Schweitz * Support copy semantics for Numpy 2.0 * DCO Remediation Commit for Anna Gringauze I, Anna Gringauze , hereby add my Signed-off-by to this commit: 85fae3771663c4c2e8659fa5c2110ef87d362efd Signed-off-by: Anna Gringauze * Try fixing doc gen and c++ compilation errors Signed-off-by: Anna Gringauze * Add compilation test for nvcc Signed-off-by: Anna Gringauze * Remove temp file Signed-off-by: Anna Gringauze * store changes Signed-off-by: A.M. Santana * first pass of updating return types to cudaq tensor Signed-off-by: A.M. Santana * store working version with tests before rebase Signed-off-by: A.M. Santana * fix improper merge resolution issues Signed-off-by: A.M. Santana * more issues with resolving merge Signed-off-by: A.M. Santana * first pass of translation to matrix_2 with build errors fixed. now double checking tests Signed-off-by: A.M. Santana * fix remaining artifacts from rebase Signed-off-by: A.M. Santana * fix matrix checks for simple elementary op unit tests Signed-off-by: A.M. Santana * first pass of implementing deeper matrix checks in tests. Have two files left to finish Signed-off-by: A.M. Santana * fix copyright headers Signed-off-by: A.M. Santana * minor change to check verified commit Signed-off-by: A.M. Santana --------- Signed-off-by: A.M. Santana Signed-off-by: Alex McCaskey Signed-off-by: Eric Schweitz Signed-off-by: Anna Gringauze Co-authored-by: Sachin Pisal Co-authored-by: cuda-quantum-bot Co-authored-by: Alex McCaskey Co-authored-by: Eric Schweitz Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Co-authored-by: Anna Gringauze --- docs/sphinx/api/languages/cpp_api.rst | 47 ++ runtime/cudaq/CMakeLists.txt | 5 +- runtime/cudaq/definition.h | 136 ++++ runtime/cudaq/dynamics/CMakeLists.txt | 37 ++ runtime/cudaq/dynamics/definition.cpp | 37 ++ .../cudaq/dynamics/elementary_operators.cpp | 469 ++++++++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 368 +++++++++++ runtime/cudaq/dynamics/product_operators.cpp | 271 ++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 275 ++++++++ runtime/cudaq/dynamics/schedule.cpp | 81 +++ runtime/cudaq/operator_utils.h | 40 ++ runtime/cudaq/operators.h | 464 ++++++++++++++ runtime/cudaq/schedule.h | 105 +++ runtime/cudaq/utils/tensor.h | 3 + unittests/CMakeLists.txt | 26 + .../dynamics/elementary_ops_arithmetic.cpp | 604 ++++++++++++++++++ unittests/dynamics/elementary_ops_simple.cpp | 208 ++++++ unittests/dynamics/operator_sum.cpp | 572 +++++++++++++++++ .../dynamics/product_operators_arithmetic.cpp | 591 +++++++++++++++++ unittests/dynamics/scalar_ops_arithmetic.cpp | 505 +++++++++++++++ unittests/dynamics/scalar_ops_simple.cpp | 119 ++++ 21 files changed, 4961 insertions(+), 2 deletions(-) create mode 100644 runtime/cudaq/definition.h create mode 100644 runtime/cudaq/dynamics/CMakeLists.txt create mode 100644 runtime/cudaq/dynamics/definition.cpp create mode 100644 runtime/cudaq/dynamics/elementary_operators.cpp create mode 100644 runtime/cudaq/dynamics/operator_sum.cpp create mode 100644 runtime/cudaq/dynamics/product_operators.cpp create mode 100644 runtime/cudaq/dynamics/scalar_operators.cpp create mode 100644 runtime/cudaq/dynamics/schedule.cpp create mode 100644 runtime/cudaq/operator_utils.h create mode 100644 runtime/cudaq/operators.h create mode 100644 runtime/cudaq/schedule.h create mode 100644 unittests/dynamics/elementary_ops_arithmetic.cpp create mode 100644 unittests/dynamics/elementary_ops_simple.cpp create mode 100644 unittests/dynamics/operator_sum.cpp create mode 100644 unittests/dynamics/product_operators_arithmetic.cpp create mode 100644 unittests/dynamics/scalar_ops_arithmetic.cpp create mode 100644 unittests/dynamics/scalar_ops_simple.cpp diff --git a/docs/sphinx/api/languages/cpp_api.rst b/docs/sphinx/api/languages/cpp_api.rst index 34487dbefb..b4c12fac2b 100644 --- a/docs/sphinx/api/languages/cpp_api.rst +++ b/docs/sphinx/api/languages/cpp_api.rst @@ -223,6 +223,53 @@ Utilities .. doxygentypedef:: cudaq::real .. doxygenfunction:: cudaq::range(std::size_t) + +Dynamics +========= + +.. .. doxygenclass:: cudaq::EvolveResult + :members: + +.. .. doxygenclass:: cudaq::AsyncEvolveResult + :members: + +.. doxygenclass:: cudaq::operator_sum + :members: + +.. doxygenclass:: cudaq::product_operator + :members: + +.. doxygenclass:: cudaq::scalar_operator + :members: + +.. doxygenclass:: cudaq::elementary_operator + :members: + +.. doxygenclass:: cudaq::OperatorArithmetics + :members: + +.. doxygenclass:: cudaq::MatrixArithmetics + :members: + +.. doxygenclass:: cudaq::Schedule + :members: + +.. doxygenclass:: cudaq::operators + :members: + +.. doxygenclass:: cudaq::pauli + :members: + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, state initial_state, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, std::vector initial_states, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve_async + Namespaces =========== diff --git a/runtime/cudaq/CMakeLists.txt b/runtime/cudaq/CMakeLists.txt index a4eac4f89d..d2f23bd0f9 100644 --- a/runtime/cudaq/CMakeLists.txt +++ b/runtime/cudaq/CMakeLists.txt @@ -40,7 +40,7 @@ if (CUDA_FOUND) PRIVATE .) target_link_libraries(${LIBRARY_NAME} - PUBLIC dl cudaq-spin cudaq-common cudaq-nlopt cudaq-ensmallen + PUBLIC dl cudaq-spin cudaq-operators cudaq-common cudaq-nlopt cudaq-ensmallen PRIVATE nvqir fmt::fmt-header-only CUDA::cudart_static) target_compile_definitions(${LIBRARY_NAME} PRIVATE CUDAQ_HAS_CUDA) @@ -52,7 +52,7 @@ else() PRIVATE .) target_link_libraries(${LIBRARY_NAME} - PUBLIC dl cudaq-spin cudaq-common cudaq-nlopt cudaq-ensmallen + PUBLIC dl cudaq-spin cudaq-operators cudaq-common cudaq-nlopt cudaq-ensmallen PRIVATE nvqir fmt::fmt-header-only) endif() @@ -61,6 +61,7 @@ add_subdirectory(algorithms) add_subdirectory(platform) add_subdirectory(builder) add_subdirectory(domains) +add_subdirectory(dynamics) install(TARGETS ${LIBRARY_NAME} EXPORT cudaq-targets DESTINATION lib) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h new file mode 100644 index 0000000000..bdf5af8ab5 --- /dev/null +++ b/runtime/cudaq/definition.h @@ -0,0 +1,136 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/qis/state.h" +#include "cudaq/utils/tensor.h" + +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +// Limit the signature of the users callback function to accept a vector of ints +// for the degree of freedom dimensions, and a vector of complex doubles for the +// concrete parameter values. +using Func = std::function, std::map>)>; + +class CallbackFunction { +private: + // The user provided callback function that takes the degrees of + // freedom and a vector of complex parameters. + Func _callback_func; + +public: + CallbackFunction() = default; + + template + CallbackFunction(Callable &&callable) { + static_assert( + std::is_invocable_r_v, + std::map>>, + "Invalid callback function. Must have signature " + "matrix_2(" + "std::map, " + "std::map>)"); + _callback_func = std::forward(callable); + } + + // Copy constructor. + CallbackFunction(CallbackFunction &other) { + _callback_func = other._callback_func; + } + + CallbackFunction(const CallbackFunction &other) { + _callback_func = other._callback_func; + } + + matrix_2 + operator()(std::map degrees, + std::map> parameters) const { + return _callback_func(std::move(degrees), std::move(parameters)); + } +}; + +using ScalarFunc = std::function( + std::map>)>; + +// A scalar callback function does not need to accept the dimensions, +// therefore we will use a different function type for this specific class. +class ScalarCallbackFunction : CallbackFunction { +private: + // The user provided callback function that takes a vector of parameters. + ScalarFunc _callback_func; + +public: + ScalarCallbackFunction() = default; + + template + ScalarCallbackFunction(Callable &&callable) { + static_assert( + std::is_invocable_r_v, Callable, + std::map>>, + "Invalid callback function. Must have signature std::complex(" + "std::map>)"); + _callback_func = std::forward(callable); + } + + // Copy constructor. + ScalarCallbackFunction(ScalarCallbackFunction &other) { + _callback_func = other._callback_func; + } + + ScalarCallbackFunction(const ScalarCallbackFunction &other) { + _callback_func = other._callback_func; + } + + bool operator!() { return (!_callback_func); } + + std::complex + operator()(std::map> parameters) const { + return _callback_func(std::move(parameters)); + } +}; + +/// @brief Object used to give an error if a Definition of an elementary +/// or scalar operator is instantiated by other means than the `define` +/// class method. +class Definition { +public: + std::string id; + + // The user-provided generator function should take a variable number of + // complex doubles for the parameters. It should return a + // `cudaq::tensor` type representing the operator + // matrix. + CallbackFunction generator; + + // Constructor. + Definition(); + + // Destructor. + ~Definition(); + + void create_definition(const std::string &operator_id, + std::map expected_dimensions, + CallbackFunction &&create); + + // To call the generator function + matrix_2 generate_matrix( + const std::map °rees, + const std::map> ¶meters) const; + +private: + // Member variables + std::map m_expected_dimensions; +}; +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt new file mode 100644 index 0000000000..9709cd9a71 --- /dev/null +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -0,0 +1,37 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +set(LIBRARY_NAME cudaq-operators) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") +set(INTERFACE_POSITION_INDEPENDENT_CODE ON) + +set(CUDAQ_OPS_SRC + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp +) + +add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) +set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) +target_include_directories(${LIBRARY_NAME} + PUBLIC + $ + $ + $ + PRIVATE .) + +set (OPERATOR_DEPENDENCIES "") +list(APPEND OPERATOR_DEPENDENCIES fmt::fmt-header-only) +add_openmp_configurations(${LIBRARY_NAME} OPERATOR_DEPENDENCIES) + +target_link_libraries(${LIBRARY_NAME} PRIVATE ${OPERATOR_DEPENDENCIES}) + +install(TARGETS ${LIBRARY_NAME} EXPORT cudaq-operator-targets DESTINATION lib) + +install(EXPORT cudaq-operator-targets + FILE CUDAQSpinTargets.cmake + NAMESPACE cudaq:: + DESTINATION lib/cmake/cudaq) diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp new file mode 100644 index 0000000000..cc357fbeab --- /dev/null +++ b/runtime/cudaq/dynamics/definition.cpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/definition.h" +#include "cudaq/qis/state.h" + +#include +#include +#include +#include + +namespace cudaq { + +Definition::Definition() = default; + +// Convenience setter +void Definition::create_definition(const std::string &operator_id, + std::map expected_dimensions, + CallbackFunction &&create) { + id = operator_id; + generator = std::move(create); + m_expected_dimensions = std::move(expected_dimensions); +} + +matrix_2 Definition::generate_matrix( + const std::map °rees, + const std::map> ¶meters) const { + return generator(degrees, parameters); +} + +Definition::~Definition() = default; +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp new file mode 100644 index 0000000000..137dde02cc --- /dev/null +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -0,0 +1,469 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include + +namespace cudaq { + +/// Elementary Operator constructor. +elementary_operator::elementary_operator(std::string operator_id, + std::vector degrees) + : id(operator_id), degrees(degrees) {} +elementary_operator::elementary_operator(const elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} +elementary_operator::elementary_operator(elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} + +elementary_operator elementary_operator::identity(int degree) { + std::string op_id = "identity"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + int degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + + // Build up the identity matrix. + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = 1.0 + 0.0 * 'j'; + } + + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::zero(int degree) { + std::string op_id = "zero"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + // Need to set the degree via the op itself because the + // argument to the outer function goes out of scope when + // the user invokes this later on via, e.g, `to_matrix()`. + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::annihilate(int degree) { + std::string op_id = "annihilate"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::create(int degree) { + std::string op_id = "create"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::position(int degree) { + std::string op_id = "position"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + // position = 0.5 * (create + annihilate) + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = + 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::momentum(int degree) { + std::string op_id = "momentum"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + // momentum = 0.5j * (create - annihilate) + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::number(int degree) { + std::string op_id = "number"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = static_cast(i) + 0.0j; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::parity(int degree) { + std::string op_id = "parity"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator +elementary_operator::displace(int degree, std::complex amplitude) { + std::string op_id = "displace"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + // if (op.m_ops.find(op_id) == op.m_ops.end()) { + // auto func = [&](std::map dimensions, + // std::map> _none) { + // auto degree = op.degrees[0]; + // std::size_t dimension = dimensions[degree]; + // auto temp_mat = matrix_2(dimension, dimension); + // // // displace = exp[ (amplitude * create) - (conj(amplitude) * + // annihilate) ] + // // for (std::size_t i = 0; i + 1 < dimension; i++) { + // // temp_mat[{i + 1, i}] = + // // amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + // // temp_mat[{i, i + 1}] = + // // -1. * std::conj(amplitude) * std::sqrt(static_cast(i + + // 1)) + + // // 0.0 * 'j'; + // // } + // // Not ideal that our method of computing the matrix exponential + // // requires copies here. Maybe we can just use eigen directly here + // // to limit to one copy, but we can address that later. + // auto mat = temp_mat.exp(); + // std::cout << "dumping the complex mat: \n"; + // mat.dump(); + // std::cout << "\ndone\n\n"; + // return mat; + // }; + // op.define(op_id, op.expected_dimensions, func); + // } + throw std::runtime_error("currently have a bug in implementation."); + return op; +} + +elementary_operator +elementary_operator::squeeze(int degree, std::complex amplitude) { + throw std::runtime_error("Not yet implemented."); +} + +matrix_2 elementary_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + return m_ops[id].generator(dimensions, parameters); +} + +/// Elementary Operator Arithmetic. + +operator_sum elementary_operator::operator+(scalar_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + std::vector> _this = { + *this}; + std::vector> _other = { + other}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +operator_sum elementary_operator::operator-(scalar_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + std::vector> _this = { + *this}; + std::vector> _other = { + -1. * other}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +product_operator elementary_operator::operator*(scalar_operator other) { + std::vector> _args = { + *this, other}; + return product_operator(_args); +} + +operator_sum elementary_operator::operator+(std::complex other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator(other); + std::vector> _this = { + *this}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +operator_sum elementary_operator::operator-(std::complex other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator((-1. * other)); + std::vector> _this = { + *this}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +product_operator elementary_operator::operator*(std::complex other) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + *this, other_scalar}; + return product_operator(_args); +} + +operator_sum elementary_operator::operator+(double other) { + std::complex value(other, 0.0); + return *this + value; +} + +operator_sum elementary_operator::operator-(double other) { + std::complex value(other, 0.0); + return *this - value; +} + +product_operator elementary_operator::operator*(double other) { + std::complex value(other, 0.0); + return *this * value; +} + +operator_sum operator+(std::complex other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(std::complex other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(std::complex other, + elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +operator_sum operator+(double other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(double other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(double other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +product_operator elementary_operator::operator*(elementary_operator other) { + std::vector> _args = { + *this, other}; + return product_operator(_args); +} + +operator_sum elementary_operator::operator+(elementary_operator other) { + std::vector> _this = { + *this}; + std::vector> _other = { + other}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +operator_sum elementary_operator::operator-(elementary_operator other) { + std::vector> _this = { + *this}; + return operator_sum({product_operator(_this), (-1. * other)}); +} + +operator_sum elementary_operator::operator+(operator_sum other) { + std::vector> _this = { + *this}; + std::vector _prods = {product_operator(_this)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + other; +} + +operator_sum elementary_operator::operator-(operator_sum other) { + std::vector> _this = { + *this}; + std::vector _prods = {product_operator(_this)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - other; +} + +operator_sum elementary_operator::operator*(operator_sum other) { + std::vector> _this = { + *this}; + std::vector _prods = {product_operator(_this)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * other; +} + +operator_sum elementary_operator::operator+(product_operator other) { + std::vector> _this = { + *this}; + return operator_sum({product_operator(_this), other}); +} + +operator_sum elementary_operator::operator-(product_operator other) { + return *this + (-1. * other); +} + +product_operator elementary_operator::operator*(product_operator other) { + std::vector> other_terms = + other.get_terms(); + /// Insert this elementary operator to the front of the terms list. + other_terms.insert(other_terms.begin(), *this); + return product_operator(other_terms); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp new file mode 100644 index 0000000000..a0ba70cb2b --- /dev/null +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include + +namespace cudaq { + +/// Operator sum constructor given a vector of product operators. +operator_sum::operator_sum(const std::vector &terms) + : m_terms(terms) {} + +// std::vector> +// operator_sum::canonicalize_product(product_operator &prod) const { +// std::vector> +// canonicalized_terms; + +// std::vector all_degrees; +// std::vector scalars; +// std::vector non_scalars; + +// for (const auto &op : prod.get_terms()) { +// if (std::holds_alternative(op)) { +// scalars.push_back(*std::get(op)); +// } else { +// non_scalars.push_back(*std::get(op)); +// all_degrees.insert(all_degrees.end(), +// std::get(op).degrees.begin(), +// std::get(op).degrees.end()); +// } +// } + +// if (all_degrees.size() == +// std::set(all_degrees.begin(), all_degrees.end()).size()) { +// std::sort(non_scalars.begin(), non_scalars.end(), +// [](const elementary_operator &a, const elementary_operator &b) { +// return a.degrees < b.degrees; +// }); +// } + +// for (size_t i = 0; std::min(scalars.size(), non_scalars.size()); i++) { +// canonicalized_terms.push_back(std::make_tuple(scalars[i], non_scalars[i])); +// } + +// return canonicalized_terms; +// } + +// std::vector> +// operator_sum::_canonical_terms() const { +// std::vector> terms; +// // for (const auto &term : m_terms) { +// // auto canonicalized = canonicalize_product(term); +// // terms.insert(terms.end(), canonicalized.begin(), canonicalized.end()); +// // } + +// // std::sort(terms.begin(), terms.end(), [](const auto &a, const auto &b) { +// // // return std::to_string(product_operator(a)) < +// // // std::to_string(product_operator(b)); +// // return product_operator(a).to_string() < +// product_operator(b).to_string(); +// // }); + +// return terms; +// } + +// operator_sum operator_sum::canonicalize() const { +// std::vector canonical_terms; +// for (const auto &term : _canonical_terms()) { +// canonical_terms.push_back(product_operator(term)); +// } +// return operator_sum(canonical_terms); +// } + +// bool operator_sum::operator==(const operator_sum &other) const { +// return _canonical_terms() == other._canonical_terms(); +// } + +// // Degrees property +// std::vector operator_sum::degrees() const { +// std::set unique_degrees; +// for (const auto &term : m_terms) { +// for (const auto &op : term.get_terms()) { +// unique_degrees.insert(op.get_degrees().begin(), +// op.get_degrees().end()); +// } +// } + +// return std::vector(unique_degrees.begin(), unique_degrees.end()); +// } + +// // Parameters property +// std::map operator_sum::parameters() const { +// std::map param_map; +// for (const auto &term : m_terms) { +// for (const auto &op : term.get_terms()) { +// auto op_params = op.parameters(); +// param_map.insert(op_params.begin(), op.params.end()); +// } +// } + +// return param_map; +// } + +// // Check if all terms are spin operators +// bool operator_sum::_is_spinop() const { +// return std::all_of( +// m_terms.begin(), m_terms.end(), [](product_operator &term) { +// return std::all_of(term.get_terms().begin(), +// term.get_terms().end(), +// [](const Operator &op) { return op.is_spinop(); +// }); +// }); +// } + +// Arithmetic operators +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector combined_terms = m_terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.m_terms.begin()), + std::make_move_iterator(other.m_terms.end())); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator-(const operator_sum &other) const { + return *this + (-1 * other); +} + +operator_sum operator_sum::operator-=(const operator_sum &other) { + *this = *this - other; + return *this; +} + +operator_sum operator_sum::operator+=(const operator_sum &other) { + *this = *this + other; + return *this; +} + +operator_sum operator_sum::operator*(operator_sum &other) const { + auto self_terms = m_terms; + std::vector product_terms; + auto other_terms = other.get_terms(); + for (auto &term : self_terms) { + for (auto &other_term : other_terms) { + product_terms.push_back(term * other_term); + } + } + return operator_sum(product_terms); +} + +operator_sum operator_sum::operator*=(operator_sum &other) { + *this = *this * other; + return *this; +} + +operator_sum operator_sum::operator*(const scalar_operator &other) const { + std::vector combined_terms = m_terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator+(const scalar_operator &other) const { + std::vector combined_terms = m_terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator-(const scalar_operator &other) const { + return *this + (-1.0 * other); +} + +operator_sum operator_sum::operator*=(const scalar_operator &other) { + *this = *this * other; + return *this; +} + +operator_sum operator_sum::operator+=(const scalar_operator &other) { + *this = *this + other; + return *this; +} + +operator_sum operator_sum::operator-=(const scalar_operator &other) { + *this = *this - other; + return *this; +} + +operator_sum operator_sum::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +operator_sum operator_sum::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +operator_sum operator_sum::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +operator_sum operator_sum::operator*=(std::complex other) { + *this *= scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator+=(std::complex other) { + *this += scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator-=(std::complex other) { + *this -= scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator*(double other) const { + return *this * scalar_operator(other); +} + +operator_sum operator_sum::operator+(double other) const { + return *this + scalar_operator(other); +} + +operator_sum operator_sum::operator-(double other) const { + return *this - scalar_operator(other); +} + +operator_sum operator_sum::operator*=(double other) { + *this *= scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator+=(double other) { + *this += scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator-=(double other) { + *this -= scalar_operator(other); + return *this; +} + +operator_sum operator*(std::complex other, operator_sum self) { + return scalar_operator(other) * self; +} + +operator_sum operator+(std::complex other, operator_sum self) { + return scalar_operator(other) + self; +} + +operator_sum operator-(std::complex other, operator_sum self) { + return scalar_operator(other) - self; +} + +operator_sum operator*(double other, operator_sum self) { + return scalar_operator(other) * self; +} + +operator_sum operator+(double other, operator_sum self) { + return scalar_operator(other) + self; +} + +operator_sum operator-(double other, operator_sum self) { + return scalar_operator(other) - self; +} + +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector combined_terms = m_terms; + combined_terms.push_back(other); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator+=(const product_operator &other) { + *this = *this + other; + return *this; +} + +operator_sum operator_sum::operator-(const product_operator &other) const { + return *this + (-1. * other); +} + +operator_sum operator_sum::operator-=(const product_operator &other) { + *this = *this - other; + return *this; +} + +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector combined_terms = m_terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator*=(const product_operator &other) { + *this = *this * other; + return *this; +} + +operator_sum operator_sum::operator+(const elementary_operator &other) const { + std::vector combined_terms = m_terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator-(const elementary_operator &other) const { + std::vector combined_terms = m_terms; + combined_terms.push_back((-1. * other)); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator*(const elementary_operator &other) const { + std::vector combined_terms = m_terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator+=(const elementary_operator &other) { + std::vector> _other = { + other}; + *this = *this + product_operator(_other); + return *this; +} + +operator_sum operator_sum::operator-=(const elementary_operator &other) { + std::vector> _other = { + other}; + *this = *this - product_operator(_other); + return *this; +} + +operator_sum operator_sum::operator*=(const elementary_operator &other) { + *this = *this * other; + return *this; +} + +/// FIXME: +// tensor +// operator_sum::to_matrix(const std::map &dimensions, +// const std::map ¶ms) const { +// // todo +// } + +// std::string operator_sum::to_string() const { +// std::string result; +// // for (const auto &term : m_terms) { +// // result += term.to_string() + " + "; +// // } +// // // Remove last " + " +// // if (!result.empty()) +// // result.pop_back(); +// return result; +// } + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp new file mode 100644 index 0000000000..0c8b411b1a --- /dev/null +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include +#include +#include + +namespace cudaq { + +/// Product Operator constructors. +product_operator::product_operator( + std::vector> + atomic_operators) + : m_terms(atomic_operators) {} + +// tensor kroneckerHelper(std::vector &matrices) { +// // essentially we pass in the list of elementary operators to +// // this function -- with lowest degree being leftmost -- then it computes +// the +// // kronecker product of all of them. +// auto kronecker = [](tensor self, tensor other) { +// return self.kronecker(other); +// }; + +// return std::accumulate(begin(matrices), end(matrices), +// tensor::identity(1, 1), kronecker); +// } + +// /// IMPLEMENT: +// tensor product_operator::to_matrix( +// std::map dimensions, +// std::map> parameters) { + +// /// TODO: This initial logic may not be needed. +// // std::vector degrees, levels; +// // for(std::map::iterator it = dimensions.begin(); it != +// // dimensions.end(); ++it) { +// // degrees.push_back(it->first); +// // levels.push_back(it->second); +// // } +// // // Calculate the size of the full Hilbert space of the given product +// // operator. int fullSize = std::accumulate(begin(levels), end(levels), 1, +// // std::multiplies()); +// std::cout << "here 49\n"; +// auto getDegrees = [](auto &&t) { return t.degrees; }; +// auto getMatrix = [&](auto &&t) { +// auto outMatrix = t.to_matrix(dimensions, parameters); +// std::cout << "dumping the outMatrix : \n"; +// outMatrix.dump(); +// return outMatrix; +// }; +// std::vector matricesFullVectorSpace; +// for (auto &term : m_terms) { +// auto op_degrees = std::visit(getDegrees, term); +// std::cout << "here 58\n"; +// // Keeps track of if we've already inserted the operator matrix +// // into the full list of matrices. +// bool alreadyInserted = false; +// std::vector matrixWithIdentities; +// /// General procedure for inserting identities: +// // * check if the operator acts on this degree by looking through +// // `op_degrees` +// // * if not, insert an identity matrix of the proper level size +// // * if so, insert the matrix itself +// for (auto [degree, level] : dimensions) { +// std::cout << "here 68\n"; +// auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); +// if (it != op_degrees.end() && !alreadyInserted) { +// std::cout << "here 71\n"; +// auto matrix = std::visit(getMatrix, term); +// std::cout << "here 75\n"; +// matrixWithIdentities.push_back(matrix); +// std::cout << "here 77\n"; +// } else { +// std::cout << "here 80\n"; +// matrixWithIdentities.push_back(tensor::identity(level, +// level)); +// } +// } +// std::cout << "here 84\n"; +// matricesFullVectorSpace.push_back(kroneckerHelper(matrixWithIdentities)); +// } +// // Now just need to accumulate with matrix multiplication all of the +// // matrices in `matricesFullVectorSpace` -- they should all be the same +// size +// // already. +// std::cout << "here 89\n"; + +// // temporary +// auto out = tensor::identity(1, 1); +// std::cout << "here 93\n"; +// return out; +// } + +// Degrees property +std::vector product_operator::degrees() const { + std::set unique_degrees; + // The variant type makes it difficult + auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; + auto endFunc = [](auto &&t) { return t.degrees.end(); }; + for (const auto &term : m_terms) { + unique_degrees.insert(std::visit(beginFunc, term), + std::visit(endFunc, term)); + } + // Erase any `-1` degree values that may have come from scalar operators. + auto it = unique_degrees.find(-1); + if (it != unique_degrees.end()) { + unique_degrees.erase(it); + } + return std::vector(unique_degrees.begin(), unique_degrees.end()); +} + +operator_sum product_operator::operator+(scalar_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +operator_sum product_operator::operator-(scalar_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); +} + +product_operator product_operator::operator*(scalar_operator other) { + std::vector> + combined_terms = m_terms; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +product_operator product_operator::operator*=(scalar_operator other) { + *this = *this * other; + return *this; +} + +operator_sum product_operator::operator+(std::complex other) { + return *this + scalar_operator(other); +} + +operator_sum product_operator::operator-(std::complex other) { + return *this - scalar_operator(other); +} + +product_operator product_operator::operator*(std::complex other) { + return *this * scalar_operator(other); +} + +product_operator product_operator::operator*=(std::complex other) { + *this = *this * scalar_operator(other); + return *this; +} + +operator_sum operator+(std::complex other, product_operator self) { + return operator_sum({scalar_operator(other), self}); +} + +operator_sum operator-(std::complex other, product_operator self) { + return scalar_operator(other) - self; +} + +product_operator operator*(std::complex other, product_operator self) { + return scalar_operator(other) * self; +} + +operator_sum product_operator::operator+(double other) { + return *this + scalar_operator(other); +} + +operator_sum product_operator::operator-(double other) { + return *this - scalar_operator(other); +} + +product_operator product_operator::operator*(double other) { + return *this * scalar_operator(other); +} + +product_operator product_operator::operator*=(double other) { + *this = *this * scalar_operator(other); + return *this; +} + +operator_sum operator+(double other, product_operator self) { + return operator_sum({scalar_operator(other), self}); +} + +operator_sum operator-(double other, product_operator self) { + return scalar_operator(other) - self; +} + +product_operator operator*(double other, product_operator self) { + return scalar_operator(other) * self; +} + +operator_sum product_operator::operator+(product_operator other) { + return operator_sum({*this, other}); +} + +operator_sum product_operator::operator-(product_operator other) { + return operator_sum({*this, (-1. * other)}); +} + +product_operator product_operator::operator*(product_operator other) { + std::vector> + combined_terms = m_terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.m_terms.begin()), + std::make_move_iterator(other.m_terms.end())); + return product_operator(combined_terms); +} + +product_operator product_operator::operator*=(product_operator other) { + *this = *this * other; + return *this; +} + +operator_sum product_operator::operator+(elementary_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +operator_sum product_operator::operator-(elementary_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); +} + +product_operator product_operator::operator*(elementary_operator other) { + std::vector> + combined_terms = m_terms; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +product_operator product_operator::operator*=(elementary_operator other) { + *this = *this * other; + return *this; +} + +operator_sum product_operator::operator+(operator_sum other) { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum product_operator::operator-(operator_sum other) { + auto negative_other = (-1. * other); + std::vector other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum product_operator::operator*(operator_sum other) { + std::vector other_terms = other.get_terms(); + for (auto &term : other_terms) { + term = *this * term; + } + return operator_sum(other_terms); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp new file mode 100644 index 0000000000..1be54ea9ee --- /dev/null +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include + +namespace cudaq { + +/// Constructors. +scalar_operator::scalar_operator(const scalar_operator &other) + : generator(other.generator), m_constant_value(other.m_constant_value) {} +scalar_operator::scalar_operator(scalar_operator &other) + : generator(other.generator), m_constant_value(other.m_constant_value) {} + +/// @brief Constructor that just takes and returns a complex double value. +scalar_operator::scalar_operator(std::complex value) { + m_constant_value = value; + auto func = [&](std::map> _none) { + return m_constant_value; + }; + generator = ScalarCallbackFunction(func); +} + +/// @brief Constructor that just takes a double and returns a complex double. +scalar_operator::scalar_operator(double value) { + std::complex castValue(value, 0.0); + m_constant_value = castValue; + auto func = [&](std::map> _none) { + return m_constant_value; + }; + generator = ScalarCallbackFunction(func); +} + +std::complex scalar_operator::evaluate( + std::map> parameters) { + return generator(parameters); +} + +matrix_2 scalar_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + auto returnOperator = matrix_2(1, 1); + returnOperator[{0, 0}] = evaluate(parameters); + return returnOperator; +} + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ + scalar_operator operator op(std::complex other, \ + scalar_operator self) { \ + /* Create an operator for the complex double value. */ \ + auto otherOperator = scalar_operator(other); \ + /* Create an operator that we will store the result in and return to the \ + * user. */ \ + scalar_operator returnOperator; \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + returnOperator._operators_to_compose.push_back(self); \ + returnOperator._operators_to_compose.push_back(otherOperator); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + /* FIXME: I have to use this hacky `.get_val()` on the newly created \ + * operator for the given complex double -- because calling the \ + * evaluate function returns 0.0 . I have no clue why??? */ \ + return returnOperator._operators_to_compose[0] \ + .evaluate(parameters) op returnOperator._operators_to_compose[1] \ + .get_val(); \ + }; \ + returnOperator.generator = ScalarCallbackFunction(newGenerator); \ + return returnOperator; \ + } + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(scalar_operator self, \ + std::complex other) { \ + /* Create an operator for the complex double value. */ \ + auto otherOperator = scalar_operator(other); \ + /* Create an operator that we will store the result in and return to the \ + * user. */ \ + scalar_operator returnOperator; \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + returnOperator._operators_to_compose.push_back(self); \ + returnOperator._operators_to_compose.push_back(otherOperator); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + /* FIXME: I have to use this hacky `.get_val()` on the newly created \ + * operator for the given complex double -- because calling the \ + * evaluate function returns 0.0 . I have no clue why??? */ \ + return returnOperator._operators_to_compose[1] \ + .get_val() op returnOperator._operators_to_compose[0] \ + .evaluate(parameters); \ + }; \ + returnOperator.generator = ScalarCallbackFunction(newGenerator); \ + return returnOperator; \ + } + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, std::complex other) { \ + /* Create an operator for the complex double value. */ \ + auto otherOperator = scalar_operator(other); \ + /* Need to move the existing generating function to a new operator so that \ + * we can modify the generator in `self` in-place. */ \ + scalar_operator copy(self); \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + self._operators_to_compose.push_back(copy); \ + self._operators_to_compose.push_back(otherOperator); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + /* FIXME: I have to use this hacky `.get_val()` on the newly created \ + * operator for the given complex double -- because calling the \ + * evaluate function returns 0.0 . I have no clue why??? */ \ + return self._operators_to_compose[0] \ + .evaluate(parameters) op self._operators_to_compose[1] \ + .get_val(); \ + }; \ + self.generator = ScalarCallbackFunction(newGenerator); \ + } + +#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ + scalar_operator operator op(double other, scalar_operator self) { \ + std::complex value(other, 0.0); \ + return self op value; \ + } + +#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ + scalar_operator operator op(scalar_operator self, double other) { \ + std::complex value(other, 0.0); \ + return value op self; \ + } + +#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, double other) { \ + std::complex value(other, 0.0); \ + self op value; \ + } + +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES(+); +ARITHMETIC_OPERATIONS_DOUBLES(-); +ARITHMETIC_OPERATIONS_DOUBLES(*); +ARITHMETIC_OPERATIONS_DOUBLES(/); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator scalar_operator::operator op(scalar_operator other) { \ + /* Create an operator that we will store the result in and return to the \ + * user. */ \ + scalar_operator returnOperator; \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + returnOperator._operators_to_compose.push_back(*this); \ + returnOperator._operators_to_compose.push_back(other); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return returnOperator._operators_to_compose[0] \ + .evaluate(parameters) op returnOperator._operators_to_compose[1] \ + .evaluate(parameters); \ + }; \ + returnOperator.generator = ScalarCallbackFunction(newGenerator); \ + return returnOperator; \ + } + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, scalar_operator other) { \ + /* Need to move the existing generating function to a new operator so \ + * that we can modify the generator in `self` in-place. */ \ + scalar_operator selfCopy(self); \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + self._operators_to_compose.push_back(selfCopy); \ + self._operators_to_compose.push_back(other); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return self._operators_to_compose[0] \ + .evaluate(parameters) op self._operators_to_compose[1] \ + .evaluate(parameters); \ + }; \ + self.generator = ScalarCallbackFunction(newGenerator); \ + } + +ARITHMETIC_OPERATIONS_SCALAR_OPS(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS(-); +ARITHMETIC_OPERATIONS_SCALAR_OPS(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); + +operator_sum scalar_operator::operator+(elementary_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum({product_operator({*this}), product_operator({other})}); +} + +operator_sum scalar_operator::operator-(elementary_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum( + {product_operator({*this}), product_operator({-1. * other})}); +} + +product_operator scalar_operator::operator*(elementary_operator other) { + return product_operator({*this, other}); +} + +operator_sum scalar_operator::operator+(product_operator other) { + return operator_sum({product_operator({*this}), other}); +} + +operator_sum scalar_operator::operator-(product_operator other) { + return operator_sum({product_operator({*this}), (-1. * other)}); +} + +product_operator scalar_operator::operator*(product_operator other) { + std::vector> other_terms = + other.get_terms(); + /// Insert this scalar operator to the front of the terms list. + other_terms.insert(other_terms.begin(), *this); + return product_operator(other_terms); +} + +operator_sum scalar_operator::operator+(operator_sum other) { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum scalar_operator::operator-(operator_sum other) { + auto negative_other = (-1. * other); + std::vector other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum scalar_operator::operator*(operator_sum other) { + std::vector other_terms = other.get_terms(); + for (auto &term : other_terms) + term = *this * term; + return operator_sum(other_terms); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/schedule.cpp b/runtime/cudaq/dynamics/schedule.cpp new file mode 100644 index 0000000000..6e833acb4f --- /dev/null +++ b/runtime/cudaq/dynamics/schedule.cpp @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/schedule.h" +#include +#include + +namespace cudaq { + +// Constructor +Schedule::Schedule(std::vector> steps, + std::vector parameters, + std::function( + const std::string &, const std::complex &)> + value_function) + : _steps(steps), _parameters(parameters), _value_function(value_function), + _current_idx(-1) { + if (!_steps.empty()) { + m_ptr = &_steps[0]; + } else { + m_ptr = nullptr; + } +} + +// Dereference operator +Schedule::reference Schedule::operator*() const { return *m_ptr; } + +// Arrow operator +Schedule::pointer Schedule::operator->() { return m_ptr; } + +// Prefix increment +Schedule &Schedule::operator++() { + if (_current_idx + 1 < static_cast(_steps.size())) { + _current_idx++; + m_ptr = &_steps[_current_idx]; + } else { + throw std::out_of_range("No more steps in the schedule."); + } + return *this; +} + +// Postfix increment +Schedule Schedule::operator++(int) { + Schedule tmp = *this; + ++(*this); + return tmp; +} + +// Comparison operators +bool operator==(const Schedule &a, const Schedule &b) { + return a.m_ptr == b.m_ptr; +}; + +bool operator!=(const Schedule &a, const Schedule &b) { + return a.m_ptr != b.m_ptr; +}; + +// Reset schedule +void Schedule::reset() { + _current_idx = -1; + if (!_steps.empty()) { + m_ptr = &_steps[0]; + } else { + m_ptr = nullptr; + } +} + +// Get the current step +std::optional> Schedule::current_step() const { + if (_current_idx >= 0 && _current_idx < static_cast(_steps.size())) { + return _steps[_current_idx]; + } + return std::nullopt; +} + +} // namespace cudaq diff --git a/runtime/cudaq/operator_utils.h b/runtime/cudaq/operator_utils.h new file mode 100644 index 0000000000..568cc8298e --- /dev/null +++ b/runtime/cudaq/operator_utils.h @@ -0,0 +1,40 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "definition.h" +#include "matrix.h" +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +inline std::map> +aggregate_parameters(const std::map ¶m1, + const std::map ¶m2) { + std::map> merged_map = param1; + + for (const auto &[key, value] : param2) { + /// FIXME: May just be able to remove this whole conditional block + /// since we're not dealing with std::string entries, but instead + /// complex doubles now. + if (merged_map.find(key) != merged_map.end()) { + // do nothing + } else { + merged_map[key] = value; + } + } + + return merged_map; +} +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h new file mode 100644 index 0000000000..77ee4703bf --- /dev/null +++ b/runtime/cudaq/operators.h @@ -0,0 +1,464 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "definition.h" +#include "utils/tensor.h" + +#include +#include +#include +#include + +namespace cudaq { + +class operator_sum; +class product_operator; +class scalar_operator; +class elementary_operator; + +/// @brief Represents an operator expression consisting of a sum of terms, where +/// each term is a product of elementary and scalar operators. Operator +/// expressions cannot be used within quantum kernels, but they provide methods +/// to convert them to data types that can. +class operator_sum { +private: + std::vector m_terms; + + std::vector> + canonicalize_product(product_operator &prod) const; + + std::vector> + _canonical_terms() const; + +public: + /// @brief Empty constructor that a user can aggregate terms into. + operator_sum() = default; + + /// @brief Construct a `cudaq::operator_sum` given a sequence of + /// `cudaq::product_operator`'s. + /// This operator expression represents a sum of terms, where each term + /// is a product of elementary and scalar operators. + operator_sum(const std::vector &terms); + + operator_sum canonicalize() const; + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + bool _is_spinop() const; + + /// TODO: implement + // template + // TEval _evaluate(OperatorArithmetics &arithmetics) const; + + /// @brief Return the `operator_sum` as a matrix. + /// @arg `dimensions` : A mapping that specifies the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0:2, 1:2}`. + /// @arg `parameters` : A map of the paramter names to their concrete, complex + /// values. + matrix_2 to_matrix(const std::map &dimensions, + const std::map ¶ms = {}) const; + + // Arithmetic operators + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; + operator_sum operator*(operator_sum &other) const; + operator_sum operator*=(operator_sum &other); + operator_sum operator+=(const operator_sum &other); + operator_sum operator-=(const operator_sum &other); + operator_sum operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + operator_sum operator*=(const scalar_operator &other); + operator_sum operator+=(const scalar_operator &other); + operator_sum operator-=(const scalar_operator &other); + operator_sum operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + operator_sum operator*=(std::complex other); + operator_sum operator+=(std::complex other); + operator_sum operator-=(std::complex other); + operator_sum operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + operator_sum operator*=(double other); + operator_sum operator+=(double other); + operator_sum operator-=(double other); + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + operator_sum operator*=(const product_operator &other); + operator_sum operator+=(const product_operator &other); + operator_sum operator-=(const product_operator &other); + operator_sum operator+(const elementary_operator &other) const; + operator_sum operator-(const elementary_operator &other) const; + operator_sum operator*(const elementary_operator &other) const; + operator_sum operator*=(const elementary_operator &other); + operator_sum operator+=(const elementary_operator &other); + operator_sum operator-=(const elementary_operator &other); + + /// @brief Return the operator_sum as a string. + std::string to_string() const; + + /// @brief Return the number of operator terms that make up this operator sum. + int term_count() const { return m_terms.size(); } + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms blockwise; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(const operator_sum &other) const; + + /// FIXME: Protect this once I can do deeper testing in unittests. + // protected: + std::vector get_terms() { return m_terms; } +}; +operator_sum operator*(std::complex other, operator_sum self); +operator_sum operator+(std::complex other, operator_sum self); +operator_sum operator-(std::complex other, operator_sum self); +operator_sum operator*(double other, operator_sum self); +operator_sum operator+(double other, operator_sum self); +operator_sum operator-(double other, operator_sum self); + +/// @brief Represents an operator expression consisting of a product of +/// elementary and scalar operators. Operator expressions cannot be used within +/// quantum kernels, but they provide methods to convert them to data types +/// that can. +class product_operator : public operator_sum { +private: + std::vector> m_terms; + +public: + product_operator() = default; + ~product_operator() = default; + + // Constructor for an operator expression that represents a product + // of scalar and elementary operators. + // arg atomic_operators : The operators of which to compute the product when + // evaluating the operator expression. + product_operator( + std::vector> + atomic_operators); + + // Arithmetic overloads against all other operator types. + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + product_operator operator*=(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + product_operator operator*=(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + product_operator operator*=(scalar_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + product_operator operator*=(product_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + product_operator operator*=(elementary_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms blockwise; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(product_operator other); + + /// @brief Return the `product_operator` as a string. + std::string to_string() const; + + /// @brief Return the `operator_sum` as a matrix. + /// @arg `dimensions` : A mapping that specifies the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0:2, 1:2}`. + /// @arg `parameters` : A map of the paramter names to their concrete, complex + /// values. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); + + /// @brief Creates a representation of the operator as a `cudaq::pauli_word` + /// that can be passed as an argument to quantum kernels. + // pauli_word to_pauli_word(); + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + /// @brief Return the number of operator terms that make up this product + /// operator. + int term_count() const { return m_terms.size(); } + + /// FIXME: Protect this once I can do deeper testing in unittests. + // protected: + std::vector> get_terms() { + return m_terms; + }; +}; +operator_sum operator+(std::complex other, product_operator self); +operator_sum operator-(std::complex other, product_operator self); +product_operator operator*(std::complex other, product_operator self); +operator_sum operator+(double other, product_operator self); +operator_sum operator-(double other, product_operator self); +product_operator operator*(double other, product_operator self); + +class elementary_operator : public product_operator { +private: + std::map m_ops; + +public: + // The constructor should never be called directly by the user: + // Keeping it internally documentd for now, however. + /// @brief Constructor. + /// @arg operator_id : The ID of the operator as specified when it was + /// defined. + /// @arg degrees : the degrees of freedom that the operator acts upon. + elementary_operator(std::string operator_id, std::vector degrees); + + // Copy constructor. + elementary_operator(const elementary_operator &other); + elementary_operator(elementary_operator &other); + + // Arithmetic overloads against all other operator types. + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator+=(operator_sum other); + operator_sum operator-=(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief True, if the other value is an elementary operator with the same id + /// acting on the same degrees of freedom, and False otherwise. + bool operator==(elementary_operator other); + + /// @brief Return the `elementary_operator` as a string. + std::string to_string() const; + + /// @brief Return the `elementary_operator` as a matrix. + /// @arg `dimensions` : A map specifying the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0 : 2, 1 : 2}`. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); + + // Predefined operators. + static elementary_operator identity(int degree); + static elementary_operator zero(int degree); + static elementary_operator annihilate(int degree); + static elementary_operator create(int degree); + static elementary_operator momentum(int degree); + static elementary_operator number(int degree); + static elementary_operator parity(int degree); + static elementary_operator position(int degree); + /// FIXME: + static elementary_operator squeeze(int degree, + std::complex amplitude); + static elementary_operator displace(int degree, + std::complex amplitude); + + /// @brief Adds the definition of an elementary operator with the given id to + /// the class. After definition, an the defined elementary operator can be + /// instantiated by providing the operator id as well as the degree(s) of + /// freedom that it acts on. An elementary operator is a parameterized object + /// acting on certain degrees of freedom. To evaluate an operator, for example + /// to compute its matrix, the level, that is the dimension, for each degree + /// of freedom it acts on must be provided, as well as all additional + /// parameters. Additional parameters must be provided in the form of keyword + /// arguments. Note: The dimensions passed during operator evaluation are + /// automatically validated against the expected dimensions specified during + /// definition - the `create` function does not need to do this. + /// @arg operator_id : A string that uniquely identifies the defined operator. + /// @arg expected_dimensions : Defines the number of levels, that is the + /// dimension, + /// for each degree of freedom in canonical (that is sorted) order. A + /// negative or zero value for one (or more) of the expected dimensions + /// indicates that the operator is defined for any dimension of the + /// corresponding degree of freedom. + /// @arg create : Takes any number of complex-valued arguments and returns the + /// matrix representing the operator in canonical order. If the matrix + /// can be defined for any number of levels for one or more degree of + /// freedom, the `create` function must take an argument called + /// `dimension` (or `dim` for short), if the operator acts on a single + /// degree of freedom, and an argument called `dimensions` (or `dims` for + /// short), if the operator acts + /// on multiple degrees of freedom. + template + void define(std::string operator_id, std::map expected_dimensions, + Func create) { + if (m_ops.find(operator_id) != m_ops.end()) { + // todo: make a nice error message to say op already exists + throw; + } + auto defn = Definition(); + defn.create_definition(operator_id, expected_dimensions, create); + m_ops[operator_id] = defn; + } + + // Attributes. + + /// @brief The number of levels, that is the dimension, for each degree of + /// freedom in canonical order that the operator acts on. A value of zero or + /// less indicates that the operator is defined for any dimension of that + /// degree. + std::map expected_dimensions; + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees; + std::string id; + + // /// @brief Creates a representation of the operator as `pauli_word` that + // can be passed as an argument to quantum kernels. + // pauli_word to_pauli_word ovveride(); +}; +// Reverse order arithmetic for elementary operators against pure scalars. +operator_sum operator+(std::complex other, elementary_operator self); +operator_sum operator-(std::complex other, elementary_operator self); +product_operator operator*(std::complex other, + elementary_operator self); +operator_sum operator+(double other, elementary_operator self); +operator_sum operator-(double other, elementary_operator self); +product_operator operator*(double other, elementary_operator self); + +class scalar_operator : public product_operator { +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::complex m_constant_value; + +public: + /// @brief Constructor that just takes a callback function with no + /// arguments. + + scalar_operator(ScalarCallbackFunction &&create) { + generator = ScalarCallbackFunction(create); + } + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value); + scalar_operator(double value); + + // Arithmetic overloads against other operator types. + scalar_operator operator+(scalar_operator other); + scalar_operator operator-(scalar_operator other); + scalar_operator operator*(scalar_operator other); + scalar_operator operator/(scalar_operator other); + /// TODO: implement and test pow + scalar_operator pow(scalar_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(std::map> parameters); + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatability with the other inherited classes. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); + + // /// @brief Returns true if other is a scalar operator with the same + // /// generator. + // bool operator==(scalar_operator other); + + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; + + // Only populated when we've performed arithmetic between various + // scalar operators. + std::vector _operators_to_compose; + + /// NOTE: We should revisit these constructors and remove any that have + /// become unecessary as the implementation improves. + scalar_operator() = default; + // Copy constructor. + scalar_operator(const scalar_operator &other); + scalar_operator(scalar_operator &other); + + ~scalar_operator() = default; + + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {-1}; + + // REMOVEME: just using this as a temporary patch: + std::complex get_val() { return m_constant_value; }; +}; + +scalar_operator operator+(scalar_operator self, std::complex other); +scalar_operator operator-(scalar_operator self, std::complex other); +scalar_operator operator*(scalar_operator self, std::complex other); +scalar_operator operator/(scalar_operator self, std::complex other); +scalar_operator operator+(std::complex other, scalar_operator self); +scalar_operator operator-(std::complex other, scalar_operator self); +scalar_operator operator*(std::complex other, scalar_operator self); +scalar_operator operator/(std::complex other, scalar_operator self); +scalar_operator operator+(scalar_operator self, double other); +scalar_operator operator-(scalar_operator self, double other); +scalar_operator operator*(scalar_operator self, double other); +scalar_operator operator/(scalar_operator self, double other); +scalar_operator operator+(double other, scalar_operator self); +scalar_operator operator-(double other, scalar_operator self); +scalar_operator operator*(double other, scalar_operator self); +scalar_operator operator/(double other, scalar_operator self); +void operator+=(scalar_operator &self, std::complex other); +void operator-=(scalar_operator &self, std::complex other); +void operator*=(scalar_operator &self, std::complex other); +void operator/=(scalar_operator &self, std::complex other); +void operator+=(scalar_operator &self, scalar_operator other); +void operator-=(scalar_operator &self, scalar_operator other); +void operator*=(scalar_operator &self, scalar_operator other); +void operator/=(scalar_operator &self, scalar_operator other); + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h new file mode 100644 index 0000000000..c9610cc75b --- /dev/null +++ b/runtime/cudaq/schedule.h @@ -0,0 +1,105 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +/// @brief Create a schedule for evaluating an operator expression at different +/// steps. +class Schedule { +public: + /// Iterator tags. May be superfluous. + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = std::complex; + using pointer = std::complex *; + using reference = std::complex &; + +private: + pointer m_ptr; + std::vector> _steps; + std::vector _parameters; + std::function(const std::string &, + const std::complex &)> + _value_function; + int _current_idx; + +public: + Schedule(pointer ptr) : m_ptr(ptr){}; + + /// @brief Constructor. + /// @arg steps: The sequence of steps in the schedule. Restricted to a vector + /// of complex values. + /// @arg parameters: A sequence of strings representing the parameter names of + /// an operator expression. + /// @arg value_function: A function that takes the name of a parameter as well + /// as an additional value ("step") of arbitrary type as argument and returns + /// the complex value for that parameter at the given step. + /// @details current_idx: Intializes the current index (_current_idx) to -1 to + /// indicate that iteration has not yet begun. Once iteration starts, + /// _current_idx will be used to track the position in the sequence of steps. + Schedule(const std::vector> steps, + const std::vector parameters, + std::function(const std::string &, + const std::complex &)> + value_function); + + /// Below, I define what I believe are the minimal necessary methods needed + /// for this to behave like an iterable. This should be revisited in the + /// implementation phase. + + // Pointers. + /// @brief Dereference operator to access the current step value. + /// @return Reference to current complex step value. + reference operator*() const; + + /// @brief Arrow operator to access the pointer the current step value. + /// @return Pointer to the current complex step value. + pointer operator->(); + + // Prefix increment. + /// @brief Prefix increment operator to move to the next step in the schedule. + /// @return Reference to the updated Schedule object. + Schedule &operator++(); + + // Postfix increment. + /// @brief Postfix increment operator to move to the next step in the + /// schedule. + /// @return Copy of the previous Schedule state. + Schedule operator++(int); + + // Comparison. + /// @brief Equality comparison operator. + /// @param a: First Schedule object. + /// @param b: Second Schedule object. + /// @return True if both schedules point to the same step, false otherwise + friend bool operator==(const Schedule &a, const Schedule &b); + + /// @brief Inequality comparison operator. + /// @param a: First Schedule object. + /// @param b: Second Schedule object. + /// @return True if both schedules point to different steps, false otherwise + friend bool operator!=(const Schedule &a, const Schedule &b); + + /// @brief Reset the schedule iterator to the beginning. + void reset(); + + /// @brief Get the current step in the schedule. + /// @return The current complex step value as an optional. If no valid step, + /// returns std::nullopt. + std::optional> current_step() const; +}; +} // namespace cudaq diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index d9f9099264..8287ab93de 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -38,6 +38,9 @@ class matrix_2 { using Dimensions = std::pair; matrix_2() = default; + matrix_2(std::size_t rows, std::size_t cols) + : dimensions(std::make_pair(rows, cols)), + data{new std::complex[rows * cols]} {} matrix_2(const matrix_2 &other) : dimensions{other.dimensions}, data{new std::complex[get_size(other.dimensions)]} { diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index fa1a9be06d..df4748f80d 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -44,6 +44,11 @@ set(CUDAQ_RUNTIME_TEST_SOURCES common/NoiseModelTester.cpp integration/tracer_tester.cpp integration/gate_library_tester.cpp + dynamics/scalar_ops_simple.cpp + dynamics/scalar_ops_arithmetic.cpp + dynamics/elementary_ops_simple.cpp + dynamics/elementary_ops_arithmetic.cpp + dynamics/product_operators_arithmetic.cpp ) # Make it so we can get function symbols @@ -258,6 +263,27 @@ target_link_libraries(test_spin gtest_main) gtest_discover_tests(test_spin) +# Create an executable for operators UnitTests +set(CUDAQ_OPERATOR_TEST_SOURCES + dynamics/operator_sum.cpp + dynamics/elementary_ops_simple.cpp + dynamics/elementary_ops_arithmetic.cpp + dynamics/scalar_ops_simple.cpp + dynamics/scalar_ops_arithmetic.cpp + dynamics/product_operators_arithmetic.cpp +) +add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_options(test_operators PRIVATE -Wl,--no-as-needed) +endif() +target_link_libraries(test_operators + PRIVATE + cudaq-spin + cudaq-operators + cudaq + gtest_main) +gtest_discover_tests(test_operators) + add_subdirectory(plugin) # build the test qudit execution manager diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp new file mode 100644 index 0000000000..fb5c85ac49 --- /dev/null +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -0,0 +1,604 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include + +/// STATUS: +/// 1. I've generated all of the `want` matrices for each test, and prepared +/// the test to check against the `got` matrix. Now waiting on finishing the +/// full `to_matrix` conversion to be able to do so. +/// + +namespace utils_0 { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils_0 + +TEST(ExpressionTester, checkPreBuiltElementaryOpsScalars) { + + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + /// Keeping these fixed for these more simple tests. + int level_count = 3; + int degree_index = 0; + double const_scale_factor = 2.0; + + // `elementary_operator + scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + // Produces an `operator_sum` type. + auto sum = self + other; + auto reverse = other + self; + + // Check the `operator_sum` attributes. + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}, {}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator + scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + // Produces an `operator_sum` type. + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator - scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + // Produces an `operator_sum` type. + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}, {}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = + scaled_identity - utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator - scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + // Produces an `operator_sum` type. + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator * scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + // Produces an `product_operator` type. + auto product = self * other; + auto reverse = other * self; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = product.to_matrix({{degree_index, level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}, {}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) * scaled_identity; + auto want_reverse_matrix = + scaled_identity * utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator * scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + // Produces an `product_operator` type. + auto product = self * other; + auto reverse = other * self; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = product.to_matrix({{degree_index, level_count}}, + // {"value", const_scale_factor}); auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) * scaled_identity; + auto want_reverse_matrix = + scaled_identity * utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } +} + +/// Prebuilt elementary ops against one another. +TEST(ExpressionTester, checkPreBuiltElementaryOpsSelf) { + + /// Keeping this fixed throughout. + int level_count = 3; + + // Addition, same DOF. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(0); + + // Produces an `operator_sum` type. + auto sum = self + other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = utils_0::annihilate_matrix(level_count) + + utils_0::create_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Addition, different DOF's. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(1); + + // Produces an `operator_sum` type. + auto sum = self + other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto annihilate_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), + utils_0::id_matrix(level_count)); + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = annihilate_full + create_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, same DOF. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(0); + + // Produces an `operator_sum` type. + auto sum = self - other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = utils_0::annihilate_matrix(level_count) - + utils_0::create_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, different DOF's. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(1); + + // Produces an `operator_sum` type. + auto sum = self - other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto annihilate_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), + utils_0::id_matrix(level_count)); + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = annihilate_full - create_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, same DOF. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(0); + + // Produces an `product_operator` type. + auto product = self * other; + ASSERT_TRUE(product.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + auto want_matrix = utils_0::annihilate_matrix(level_count) * + utils_0::create_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, different DOF's. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(1); + + // Produces an `product_operator` type. + auto product = self * other; + ASSERT_TRUE(product.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto annihilate_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), + utils_0::id_matrix(level_count)); + // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + auto want_matrix = annihilate_full * create_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } +} + +/// Testing arithmetic between elementary operators and operator +/// sums. +TEST(ExpressionTester, checkElementaryOpsAgainstOpSum) { + + /// Keeping this fixed throughout. + int level_count = 3; + + /// `elementary_operator + operator_sum` and `operator_sum + + /// elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + auto got = self + operator_sum; + auto reverse = operator_sum + self; + + ASSERT_TRUE(got.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = self_full + term_0_full + term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full + self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `elementary_operator - operator_sum` and `operator_sum - + /// elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + auto got = self - operator_sum; + auto reverse = operator_sum - self; + + ASSERT_TRUE(got.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = self_full - term_0_full - term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full - self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `elementary_operator * operator_sum` and `operator_sum * + /// elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + auto got = self * operator_sum; + auto reverse = operator_sum * self; + + ASSERT_TRUE(got.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto &term : got.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + + for (auto &term : reverse.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + auto sum_full = term_0_full + term_1_full; + + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = self_full * sum_full; + auto want_reverse_matrix = sum_full * self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `operator_sum += elementary_operator` + { + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + operator_sum += cudaq::elementary_operator::annihilate(0); + + ASSERT_TRUE(operator_sum.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = term_0_full + term_1_full + self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum -= elementary_operator` + { + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + operator_sum -= cudaq::elementary_operator::annihilate(0); + + ASSERT_TRUE(operator_sum.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = term_0_full + term_1_full - self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum *= elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + operator_sum *= self; + + ASSERT_TRUE(operator_sum.term_count() == 2); + + for (auto &term : operator_sum.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + auto sum_full = term_0_full + term_1_full; + + // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = sum_full * self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } +} diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp new file mode 100644 index 0000000000..172beeffe8 --- /dev/null +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include + +namespace utils { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils + +TEST(ExpressionTester, checkPreBuiltElementaryOps) { + std::vector levels = {2, 3, 4, 5}; + + // Keeping this fixed throughout. + int degree_index = 0; + + // Identity operator. + { + for (auto level_count : levels) { + // cudaq::operators::identity(int degree) + auto id = cudaq::elementary_operator::identity(degree_index); + auto got_id = id.to_matrix({{degree_index, level_count}}, {}); + auto want_id = utils::id_matrix(level_count); + utils::checkEqual(want_id, got_id); + } + } + + // Zero operator. + { + for (auto level_count : levels) { + auto zero = cudaq::elementary_operator::zero(degree_index); + auto got_zero = zero.to_matrix({{degree_index, level_count}}, {}); + auto want_zero = utils::zero_matrix(level_count); + utils::checkEqual(want_zero, got_zero); + } + } + + // Annihilation operator. + { + for (auto level_count : levels) { + auto annihilate = cudaq::elementary_operator::annihilate(degree_index); + auto got_annihilate = + annihilate.to_matrix({{degree_index, level_count}}, {}); + auto want_annihilate = utils::annihilate_matrix(level_count); + utils::checkEqual(want_annihilate, got_annihilate); + } + } + + // Creation operator. + { + for (auto level_count : levels) { + auto create = cudaq::elementary_operator::create(degree_index); + auto got_create = create.to_matrix({{degree_index, level_count}}, {}); + auto want_create = utils::create_matrix(level_count); + utils::checkEqual(want_create, got_create); + } + } + + // Position operator. + { + for (auto level_count : levels) { + auto position = cudaq::elementary_operator::position(degree_index); + auto got_position = position.to_matrix({{degree_index, level_count}}, {}); + auto want_position = utils::position_matrix(level_count); + utils::checkEqual(want_position, got_position); + } + } + + // Momentum operator. + { + for (auto level_count : levels) { + auto momentum = cudaq::elementary_operator::momentum(degree_index); + auto got_momentum = momentum.to_matrix({{degree_index, level_count}}, {}); + auto want_momentum = utils::momentum_matrix(level_count); + utils::checkEqual(want_momentum, got_momentum); + } + } + + // Number operator. + { + for (auto level_count : levels) { + auto number = cudaq::elementary_operator::number(degree_index); + auto got_number = number.to_matrix({{degree_index, level_count}}, {}); + auto want_number = utils::number_matrix(level_count); + utils::checkEqual(want_number, got_number); + } + } + + // Parity operator. + { + for (auto level_count : levels) { + auto parity = cudaq::elementary_operator::parity(degree_index); + auto got_parity = parity.to_matrix({{degree_index, level_count}}, {}); + auto want_parity = utils::parity_matrix(level_count); + utils::checkEqual(want_parity, got_parity); + } + } + + // // // Displacement operator. + // // { + // // for (auto level_count : levels) { + // // auto amplitude = 1.0 + 1.0j; + // // auto displace = cudaq::elementary_operator::displace(degree_index, + // // amplitude); auto got_displace = + // utils::displace.to_matrix({{degree_index, + // // level_count}}, {}); auto want_displace = + // displace_matrix(level_count, + // // amplitude); utils::checkEqual(want_displace, got_displace); + // // } + // // } + + // TODO: Squeeze operator. +} + +// TEST(ExpressionTester, checkCustomElementaryOps) { +// // pass +// } \ No newline at end of file diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp new file mode 100644 index 0000000000..c68779ef45 --- /dev/null +++ b/unittests/dynamics/operator_sum.cpp @@ -0,0 +1,572 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +namespace utils_2 { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils_2 + +// TEST(ExpressionTester, checkProductOperatorSimple) { +// std::vector levels = {2, 3, 4}; + +// // std::set uniqueDegrees; +// // std::copy(this->degrees.begin(), this->degrees.end(), +// // std::inserter(uniqueDegrees, uniqueDegrees.begin())); +// // std::copy(other.degrees.begin(), other.degrees.end(), +// // std::inserter(uniqueDegrees, uniqueDegrees.begin())); + +// // Arithmetic only between elementary operators with +// // same number of levels. +// { +// // Same degrees of freedom. +// { +// for (auto level_count : levels) { +// auto op0 = cudaq::elementary_operator::annihilate(0); +// auto op1 = cudaq::elementary_operator::create(0); + +// cudaq::product_operator got = op0 * op1; +// auto got_matrix = got.to_matrix({{0, level_count}}, {}); + +// auto matrix0 = _annihilate_matrix(level_count); +// auto matrix1 = _create_matrix(level_count); +// auto want_matrix = matrix0 * matrix1; + +// // ASSERT_TRUE(want_matrix == got_matrix); +// } +// } + +// // // Different degrees of freedom. +// // { +// // for (auto level_count : levels) { +// // auto op0 = cudaq::elementary_operator::annihilate(0); +// // auto op1 = cudaq::elementary_operator::create(1); + +// // cudaq::product_operator got = op0 * op1; +// // auto got_matrix = +// // got.to_matrix({{0, level_count}, {1, level_count}}, {}); + +// // cudaq::product_operator got_reverse = op1 * op0; +// // auto got_matrix_reverse = +// // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, +// {}); + +// // auto identity = _id_matrix(level_count); +// // auto matrix0 = _annihilate_matrix(level_count); +// // auto matrix1 = _create_matrix(level_count); + +// // auto fullHilbert0 = identity.kronecker(matrix0); +// // auto fullHilbert1 = matrix1.kronecker(identity); +// // auto want_matrix = fullHilbert0 * fullHilbert1; +// // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + +// // // ASSERT_TRUE(want_matrix == got_matrix); +// // // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); +// // } +// // } + +// // // Different degrees of freedom, non-consecutive. +// // { +// // for (auto level_count : levels) { +// // auto op0 = cudaq::elementary_operator::annihilate(0); +// // auto op1 = cudaq::elementary_operator::create(2); + +// // // cudaq::product_operator got = op0 * op1; +// // // auto got_matrix = +// got.to_matrix({{0,level_count},{2,level_count}}, +// // // {}); +// // } +// // } + +// // // Different degrees of freedom, non-consecutive but all dimensions +// // // provided. +// // { +// // for (auto level_count : levels) { +// // auto op0 = cudaq::elementary_operator::annihilate(0); +// // auto op1 = cudaq::elementary_operator::create(2); + +// // // cudaq::product_operator got = op0 * op1; +// // // auto got_matrix = +// // // +// got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, +// // {}); +// // } +// // } +// } +// } + +// TEST(ExpressionTester, checkProductOperatorSimple) { + +// std::complex value_0 = 0.1 + 0.1; +// std::complex value_1 = 0.1 + 1.0; +// std::complex value_2 = 2.0 + 0.1; +// std::complex value_3 = 2.0 + 1.0; + +// auto local_variable = true; +// auto function = [&](std::map> parameters) +// { +// if (!local_variable) +// throw std::runtime_error("Local variable not detected."); +// return parameters["value"]; +// }; + +// // Scalar Ops against Elementary Ops +// { +// // Identity against constant. +// { +// auto id_op = cudaq::elementary_operator::identity(0); +// auto scalar_op = cudaq::scalar_operator(value_0); + +// // auto multiplication = scalar_op * id_op; +// // auto addition = scalar_op + id_op; +// // auto subtraction = scalar_op - id_op; +// } + +// // Identity against constant from lambda. +// { +// auto id_op = cudaq::elementary_operator::identity(0); +// auto scalar_op = cudaq::scalar_operator(function); + +// // auto multiplication = scalar_op * id_op; +// // auto addition = scalar_op + id_op; +// // auto subtraction = scalar_op - id_op; +// } +// } +// } + +TEST(ExpressionTester, checkOperatorSumAgainstScalarOperator) { + + // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = sum * cudaq::scalar_operator(1.0); + auto reverse = cudaq::scalar_operator(1.0) * sum; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = original + cudaq::scalar_operator(1.0); + auto reverse = cudaq::scalar_operator(1.0) + original; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = original - cudaq::scalar_operator(1.0); + auto reverse = cudaq::scalar_operator(1.0) - original; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum *= scalar_operator` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= cudaq::scalar_operator(1.0); + + ASSERT_TRUE(sum.term_count() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum += scalar_operator` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += cudaq::scalar_operator(1.0); + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= scalar_operator` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= cudaq::scalar_operator(1.0); + + ASSERT_TRUE(sum.term_count() == 3); + } +} + +TEST(ExpressionTester, checkOperatorSumAgainstScalars) { + std::complex value = 0.1 + 0.1; + + // `operator_sum * double` and `double * operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = sum * 2.0; + auto reverse = 2.0 * sum; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum + double` and `double + operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = original + 2.0; + auto reverse = 2.0 + original; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum - double` and `double - operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = original - 2.0; + auto reverse = 2.0 - original; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum *= double` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= 2.0; + + ASSERT_TRUE(sum.term_count() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum += double` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += 2.0; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= double` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= 2.0; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum * std::complex` and `std::complex * + // operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = sum * value; + auto reverse = value * sum; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum + std::complex` and `std::complex + + // operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = original + value; + auto reverse = value + original; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum - std::complex` and `std::complex - + // operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = original - value; + auto reverse = value - original; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum *= std::complex` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= value; + + ASSERT_TRUE(sum.term_count() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum += std::complex` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += value; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= std::complex` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= value; + + ASSERT_TRUE(sum.term_count() == 3); + } +} + +TEST(ExpressionTester, checkOperatorSumAgainstOperatorSum) { + // `operator_sum + operator_sum` + { + auto sum_0 = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(3); + + auto sum = sum_0 + sum_1; + + ASSERT_TRUE(sum.term_count() == 5); + } + + // `operator_sum - operator_sum` + { + auto sum_0 = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(2); + + auto difference = sum_0 - sum_1; + + ASSERT_TRUE(difference.term_count() == 5); + } + + // `operator_sum * operator_sum` + { + auto sum_0 = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(2); + + auto sum_product = sum_0 * sum_1; + + ASSERT_TRUE(sum_product.term_count() == 6); + for (auto term : sum_product.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + } + + // `operator_sum *= operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(2); + + sum *= sum_1; + + ASSERT_TRUE(sum.term_count() == 6); + for (auto term : sum.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + } +} + +/// NOTE: Much of the simpler arithmetic between the two is tested in the +/// product operator test file. This mainly just tests the assignment operators +/// between the two types. +TEST(ExpressionTester, checkOperatorSumAgainstProduct) { + // `operator_sum += product_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += product; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= product_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= product; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum *= product_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= product; + + ASSERT_TRUE(sum.term_count() == 2); + + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 3); + } + } +} diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp new file mode 100644 index 0000000000..f8b6be7742 --- /dev/null +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -0,0 +1,591 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +#include + +namespace utils_1 { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils_1 + +/// TODO: Not yet testing the output matrices coming from this arithmetic. + +TEST(ExpressionTester, checkProductOperatorSimpleMatrixChecks) { + std::vector levels = {2, 3, 4}; + + { + // Same degrees of freedom. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(0); + + cudaq::product_operator got = op0 * op1; + + // auto got_matrix = got.to_matrix({{0, level_count}}, {}); + + // auto matrix0 = _annihilate_matrix(level_count); + // auto matrix1 = _create_matrix(level_count); + // auto want_matrix = matrix0 * matrix1; + + // ASSERT_TRUE(want_matrix == got_matrix); + + std::vector want_degrees = {0}; + ASSERT_TRUE(got.degrees() == want_degrees); + } + } + + // Different degrees of freedom. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(1); + + cudaq::product_operator got = op0 * op1; + // auto got_matrix = + // got.to_matrix({{0, level_count}, {1, level_count}}, {}); + + cudaq::product_operator got_reverse = op1 * op0; + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); + + // auto identity = _id_matrix(level_count); + // auto matrix0 = _annihilate_matrix(level_count); + // auto matrix1 = _create_matrix(level_count); + + // auto fullHilbert0 = identity.kronecker(matrix0); + // auto fullHilbert1 = matrix1.kronecker(identity); + // auto want_matrix = fullHilbert0 * fullHilbert1; + // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // ASSERT_TRUE(want_matrix == got_matrix); + // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); + + std::vector want_degrees = {0, 1}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } + + // Different degrees of freedom, non-consecutive. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(2); + + cudaq::product_operator got = op0 * op1; + // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, + // {}); + + cudaq::product_operator got_reverse = op1 * op0; + + std::vector want_degrees = {0, 2}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } + + // Different degrees of freedom, non-consecutive but all dimensions + // provided. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(2); + + cudaq::product_operator got = op0 * op1; + // auto got_matrix = + // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); + + cudaq::product_operator got_reverse = op1 * op0; + + std::vector want_degrees = {0, 2}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } + } +} + +TEST(ExpressionTester, checkProductOperatorSimpleContinued) { + + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + + // Scalar Ops against Elementary Ops + { + // Annihilation against constant. + { + auto id_op = cudaq::elementary_operator::annihilate(0); + auto scalar_op = cudaq::scalar_operator(value_0); + + auto got = scalar_op * id_op; + auto got_reverse = scalar_op * id_op; + + std::vector want_degrees = {0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + + // Annihilation against constant from lambda. + { + auto id_op = cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(function); + + auto got = scalar_op * id_op; + auto got_reverse = scalar_op * id_op; + + std::vector want_degrees = {1}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstScalars) { + std::complex value_0 = 0.1 + 0.1; + + /// `product_operator + complex` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto sum = value_0 + product_op; + auto reverse = product_op + value_0; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator + double` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto sum = 2.0 + product_op; + auto reverse = product_op + 2.0; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator + scalar_operator` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + auto sum = scalar_op + product_op; + auto reverse = product_op + scalar_op; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator - complex` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto difference = value_0 - product_op; + auto reverse = product_op - value_0; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(difference.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator - double` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto difference = 2.0 - product_op; + auto reverse = product_op - 2.0; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(difference.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator - scalar_operator` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + auto difference = scalar_op - product_op; + auto reverse = product_op - scalar_op; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(difference.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator * complex` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto product = value_0 * product_op; + auto reverse = product_op * value_0; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator * double` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto product = 2.0 * product_op; + auto reverse = product_op * 2.0; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator * scalar_operator` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + auto product = scalar_op * product_op; + auto reverse = product_op * scalar_op; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator *= complex` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + product *= value_0; + + ASSERT_TRUE(product.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + } + + /// `product_operator *= double` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + product *= 2.0; + + ASSERT_TRUE(product.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + } + + /// `product_operator *= scalar_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + product *= scalar_op; + + ASSERT_TRUE(product.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstProduct) { + + // `product_operator + product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + auto sum = term_0 + term_1; + + ASSERT_TRUE(sum.term_count() == 2); + } + + // `product_operator - product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + auto difference = term_0 - term_1; + + ASSERT_TRUE(difference.term_count() == 2); + } + + // `product_operator * product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + auto product = term_0 * term_1; + + ASSERT_TRUE(product.term_count() == 4); + } + + // `product_operator *= product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + term_0 *= term_1; + + ASSERT_TRUE(term_0.term_count() == 4); + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstElementary) { + + // `product_operator + elementary_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + auto sum = product + elementary; + auto reverse = elementary + product; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + } + + // `product_operator - elementary_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + auto difference = product - elementary; + auto reverse = elementary - product; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + } + + // `product_operator * elementary_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + auto product = term_0 * elementary; + auto reverse = elementary * term_0; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `product_operator *= elementary_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + product *= elementary; + + ASSERT_TRUE(product.term_count() == 3); + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstOperatorSum) { + + // `product_operator + operator_sum` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto original_sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = product + original_sum; + auto reverse = original_sum + product; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `product_operator - operator_sum` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto original_sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = product - original_sum; + auto reverse = original_sum - product; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `product_operator * operator_sum` + { + auto original_product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = original_product * sum; + auto reverse = sum * original_product; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 3); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 3); + } + } +} diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp new file mode 100644 index 0000000000..a8d6054a32 --- /dev/null +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -0,0 +1,505 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +TEST(ExpressionTester, checkScalarOpsArithmeticDoubles) { + // Arithmetic overloads against complex doubles. + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + + // + : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + + auto new_scalar_op = value_1 + scalar_op; + // function + scalar_op; + auto reverse_order_op = scalar_op + value_1; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_1 + value_0; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op + reverse_order_op; + auto got_value_third = third_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value + want_value), + 1e-5); + } + + // + : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_0 + scalar_op; + auto reverse_order_op = scalar_op + value_0; + + auto got_value = new_scalar_op.evaluate({{"value", value_1}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 + value_0), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op + reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_1}}); + auto want_value = value_0 + value_1 + value_1 + value_0; + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // - : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_1); + + auto new_scalar_op = value_3 - scalar_op; + auto reverse_order_op = scalar_op - value_3; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op - reverse_order_op; + auto got_value_third = third_op.evaluate({}); + auto want_value = (value_3 - value_1) - (value_1 - value_3); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // - : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_2 - scalar_op; + auto reverse_order_op = scalar_op - value_2; + + auto got_value = new_scalar_op.evaluate({{"value", value_1}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 - value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_2), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op - reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_1}}); + auto want_value = (value_2 - value_1) - (value_1 - value_2); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // * : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + + auto new_scalar_op = value_3 * scalar_op; + auto reverse_order_op = scalar_op * value_3; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op * reverse_order_op; + auto got_value_third = third_op.evaluate({}); + auto want_value = (value_3 * value_2) * (value_2 * value_3); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // * : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_3 * scalar_op; + auto reverse_order_op = scalar_op * value_3; + + auto got_value = new_scalar_op.evaluate({{"value", value_2}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_2}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op * reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_2}}); + auto want_value = (value_3 * value_2) * (value_2 * value_3); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // / : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + + auto new_scalar_op = value_3 / scalar_op; + auto reverse_order_op = scalar_op / value_3; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_2), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op / reverse_order_op; + auto got_value_third = third_op.evaluate({}); + auto want_value = (value_2 / value_3) / (value_3 / value_2); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // / : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_3 / scalar_op; + auto reverse_order_op = scalar_op / value_3; + + auto got_value = new_scalar_op.evaluate({{"value", value_1}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_1 / value_3), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_1), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op / reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_1}}); + auto want_value = (value_1 / value_3) / (value_3 / value_1); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // += : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + scalar_op += value_0; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); + } + + // += : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op += value_1; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + } + + // -= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + scalar_op -= value_0; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); + } + + // -= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op -= value_1; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_1), 1e-5); + } + + // *= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // *= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // /= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } + + // /= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } +} + +TEST(ExpressionTester, checkScalarOpsArithmeticScalarOps) { + // Arithmetic overloads against other scalar ops. + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + + // I use another function here to make sure that local variables + // that may be unique to each ScalarOp's generators are both kept + // track of when we merge the generators. + auto alternative_local_variable = true; + auto alternative_function = + [&](std::map> parameters) { + if (!alternative_local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["other"]; + }; + + // + : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + auto other_scalar_op = cudaq::scalar_operator(value_1); + + auto new_scalar_op = other_scalar_op + scalar_op; + auto reverse_order_op = scalar_op + other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_1 + value_0; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); + } + + // + : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op + scalar_op; + auto reverse_order_op = scalar_op + other_scalar_op; + + std::map> parameter_map = { + {"value", value_1}, {"other", value_0}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 + value_0), 1e-5); + } + + // - : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + auto other_scalar_op = cudaq::scalar_operator(value_1); + + auto new_scalar_op = other_scalar_op - scalar_op; + auto reverse_order_op = scalar_op - other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_1 - value_2; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); + } + + // - : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op - scalar_op; + auto reverse_order_op = scalar_op - other_scalar_op; + + std::map> parameter_map = { + {"value", value_1}, {"other", value_3}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); + } + + // * : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + auto other_scalar_op = cudaq::scalar_operator(value_3); + + auto new_scalar_op = other_scalar_op * scalar_op; + auto reverse_order_op = scalar_op * other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_3 * value_2; + auto reverse_want_value = value_2 * value_3; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(reverse_want_value), 1e-5); + } + + // * : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op * scalar_op; + auto reverse_order_op = scalar_op * other_scalar_op; + + std::map> parameter_map = { + {"value", value_1}, {"other", value_3}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 * value_3), 1e-5); + } + + // / : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + auto other_scalar_op = cudaq::scalar_operator(value_2); + + auto new_scalar_op = other_scalar_op / scalar_op; + auto reverse_order_op = scalar_op / other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_2 / value_0; + auto reverse_want_value = value_0 / value_2; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(reverse_want_value), 1e-5); + } + + // / : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op / scalar_op; + auto reverse_order_op = scalar_op / other_scalar_op; + + std::map> parameter_map = { + {"value", value_0}, {"other", value_3}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_0), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_0 / value_3), 1e-5); + } + + // += : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + auto other = cudaq::scalar_operator(value_0); + scalar_op += other; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); + } + + // += : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other = cudaq::scalar_operator(value_1); + scalar_op += other; + + auto scalar_op_1 = cudaq::scalar_operator(function); + auto other_function = cudaq::scalar_operator(alternative_function); + scalar_op_1 += other_function; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + auto got_value_1 = + scalar_op_1.evaluate({{"value", value_0}, {"other", value_1}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_0 + value_1), 1e-5); + } + + // -= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + scalar_op -= value_0; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); + } + + // -= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op -= value_1; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_1), 1e-5); + } + + // *= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // *= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // /= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } + + // /= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } +} \ No newline at end of file diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp new file mode 100644 index 0000000000..c60414d705 --- /dev/null +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +TEST(ExpressionTester, checkScalarOpsSimpleComplex) { + + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + // From concrete values. + { + auto operator_0 = cudaq::scalar_operator(value_0); + auto operator_1 = cudaq::scalar_operator(value_1); + auto operator_2 = cudaq::scalar_operator(value_2); + auto operator_3 = cudaq::scalar_operator(value_3); + + auto got_value_0 = operator_0.evaluate({}); + auto got_value_1 = operator_1.evaluate({}); + auto got_value_2 = operator_2.evaluate({}); + auto got_value_3 = operator_3.evaluate({}); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } + + // From a lambda function. + { + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + std::map> parameter_map; + + auto operator_0 = cudaq::scalar_operator(function); + auto operator_1 = cudaq::scalar_operator(function); + auto operator_2 = cudaq::scalar_operator(function); + auto operator_3 = cudaq::scalar_operator(function); + + parameter_map["value"] = value_0; + auto got_value_0 = operator_0.evaluate(parameter_map); + parameter_map["value"] = value_1; + auto got_value_1 = operator_1.evaluate(parameter_map); + parameter_map["value"] = value_2; + auto got_value_2 = operator_2.evaluate(parameter_map); + parameter_map["value"] = value_3; + auto got_value_3 = operator_3.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } +} + +TEST(ExpressionTester, checkScalarOpsSimpleDouble) { + + double value_0 = 0.1; + double value_1 = 0.2; + double value_2 = 2.1; + double value_3 = 2.2; + + // From concrete values. + { + auto operator_0 = cudaq::scalar_operator(value_0); + auto operator_1 = cudaq::scalar_operator(value_1); + auto operator_2 = cudaq::scalar_operator(value_2); + auto operator_3 = cudaq::scalar_operator(value_3); + + auto got_value_0 = operator_0.evaluate({}); + auto got_value_1 = operator_1.evaluate({}); + auto got_value_2 = operator_2.evaluate({}); + auto got_value_3 = operator_3.evaluate({}); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } + + // From a lambda function. + { + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + std::map> parameter_map; + + auto operator_0 = cudaq::scalar_operator(function); + auto operator_1 = cudaq::scalar_operator(function); + auto operator_2 = cudaq::scalar_operator(function); + auto operator_3 = cudaq::scalar_operator(function); + + parameter_map["value"] = value_0; + auto got_value_0 = operator_0.evaluate(parameter_map); + parameter_map["value"] = value_1; + auto got_value_1 = operator_1.evaluate(parameter_map); + parameter_map["value"] = value_2; + auto got_value_2 = operator_2.evaluate(parameter_map); + parameter_map["value"] = value_3; + auto got_value_3 = operator_3.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } +} \ No newline at end of file From 4e1166984035a37abdb1ee410928661a2907ec3b Mon Sep 17 00:00:00 2001 From: cuda-quantum-bot Date: Thu, 9 Jan 2025 17:37:24 +0000 Subject: [PATCH 002/311] Cleaning up docs preview for PR #12. From b2a6682dc1581da8449d185635ed22b52dfb47f3 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 9 Jan 2025 10:05:51 -0800 Subject: [PATCH 003/311] fixing spellings Signed-off-by: Sachin Pisal --- runtime/cudaq/operators.h | 18 +++++++++--------- runtime/cudaq/schedule.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 77ee4703bf..d668909daa 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -62,8 +62,8 @@ class operator_sum { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0:2, 1:2}`. - /// @arg `parameters` : A map of the paramter names to their concrete, complex - /// values. + /// @arg `parameters` : A map of the parameter names to their concrete, + /// complex values. matrix_2 to_matrix(const std::map &dimensions, const std::map ¶ms = {}) const; @@ -116,13 +116,13 @@ class operator_sum { /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms blockwise; it may hence + /// account, and does not try to reorder terms `blockwise`; it may hence /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. bool operator==(const operator_sum &other) const; - /// FIXME: Protect this once I can do deeper testing in unittests. + /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: std::vector get_terms() { return m_terms; } }; @@ -183,7 +183,7 @@ class product_operator : public operator_sum { /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms blockwise; it may hence + /// account, and does not try to reorder terms `blockwise`; it may hence /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. @@ -197,8 +197,8 @@ class product_operator : public operator_sum { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0:2, 1:2}`. - /// @arg `parameters` : A map of the paramter names to their concrete, complex - /// values. + /// @arg `parameters` : A map of the parameter names to their concrete, + /// complex values. matrix_2 to_matrix(std::map dimensions, std::map> parameters); @@ -214,7 +214,7 @@ class product_operator : public operator_sum { /// operator. int term_count() const { return m_terms.size(); } - /// FIXME: Protect this once I can do deeper testing in unittests. + /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: std::vector> get_terms() { return m_terms; @@ -419,7 +419,7 @@ class scalar_operator : public product_operator { std::vector _operators_to_compose; /// NOTE: We should revisit these constructors and remove any that have - /// become unecessary as the implementation improves. + /// become unnecessary as the implementation improves. scalar_operator() = default; // Copy constructor. scalar_operator(const scalar_operator &other); diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h index c9610cc75b..67d68fd951 100644 --- a/runtime/cudaq/schedule.h +++ b/runtime/cudaq/schedule.h @@ -1,5 +1,5 @@ /****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * * This source code and the accompanying materials are made available under * @@ -62,7 +62,7 @@ class Schedule { /// implementation phase. // Pointers. - /// @brief Dereference operator to access the current step value. + /// @brief `Dereference` operator to access the current step value. /// @return Reference to current complex step value. reference operator*() const; @@ -76,7 +76,7 @@ class Schedule { Schedule &operator++(); // Postfix increment. - /// @brief Postfix increment operator to move to the next step in the + /// @brief `Postfix` increment operator to move to the next step in the /// schedule. /// @return Copy of the previous Schedule state. Schedule operator++(int); From 42d0aeb3afa6f78cd27f0f7562bdae1cae703116 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 9 Jan 2025 10:21:04 -0800 Subject: [PATCH 004/311] Adding base_integrator and base_time_stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 58 +++++++++++++++++++++++++++++++ runtime/cudaq/base_time_stepper.h | 19 ++++++++++ 2 files changed, 77 insertions(+) create mode 100644 runtime/cudaq/base_integrator.h create mode 100644 runtime/cudaq/base_time_stepper.h diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h new file mode 100644 index 0000000000..a4dc982752 --- /dev/null +++ b/runtime/cudaq/base_integrator.h @@ -0,0 +1,58 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include "schedule.h" +#include "operators.h" + +namespace cudaq { +template +class BaseIntegrator { +protected: + std::map integrator_options; + TState state; + double t; + std::map dimensions; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; + std::shared_ptr> stepper; + std::vector> collapse_operators; + + virtual void post_init() = 0; + +public: + virtual ~BaseIntegrator() = default; + + void set_state(const TState &initial_state, double t0 = 0.0) { + state = initial_state; + t = t0; + } + + void set_system( + const std::map &dimensions, + std::shared_ptr schedule, + std::shared_ptr hamiltonian, + std::vector> collapse_operators = {} + ) { + this->dimensions = dimensions; + this->schedule = schedule; + this->hamiltonian = hamiltonian; + this->collapse_operators = collapse_operators; + } + + virtual void integrate(double t) = 0; + + std::pair get_state() const { + return {t, state}; + } +}; +} diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h new file mode 100644 index 0000000000..d92184825b --- /dev/null +++ b/runtime/cudaq/base_time_stepper.h @@ -0,0 +1,19 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +namespace cudaq { +template +class BaseTimeStepper { +public: + virtual ~BaseTimeStepper() = default; + + virtual void compute(TState &state, double t) = 0; +}; +} \ No newline at end of file From baa1d10e57af3beb8432bba420896dfa17673ef3 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 08:21:09 -0800 Subject: [PATCH 005/311] adding base_operators interface Signed-off-by: Sachin Pisal --- runtime/cudaq/base_operator.h | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 runtime/cudaq/base_operator.h diff --git a/runtime/cudaq/base_operator.h b/runtime/cudaq/base_operator.h new file mode 100644 index 0000000000..fe604e067d --- /dev/null +++ b/runtime/cudaq/base_operator.h @@ -0,0 +1,36 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/matrix.h" +#include "cudaq/utils/tensor.h" +#include +#include +#include +#include + +namespace cudaq { +/// @brief Base class for all operator types. +class base_operator { +public: + virtual ~base_operator() = default; + + /// @brief Evaluate the operator with given parameters + virtual std::complex evaluate(const std::map> ¶meters) const = 0; + + /// @brief Convert the operator to a matrix representation. + virtual matrix_2 to_matrix(const std::map &dimensions, const std::map> ¶meters = {}) const = 0; + + /// @brief Convert the operator to a string representation. + virtual std::string to_string() const = 0; + + /// @brief Return the degrees of freedom that the operator acts on. + virtual std::vector get_degrees() const = 0; +}; +} From 463afdf02f4cccf7e26907041076ab7408ab71fb Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 10:29:33 -0800 Subject: [PATCH 006/311] keeping degrees to match the current implementation Signed-off-by: Sachin Pisal --- runtime/cudaq/base_operator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/base_operator.h b/runtime/cudaq/base_operator.h index fe604e067d..ea3475a5ee 100644 --- a/runtime/cudaq/base_operator.h +++ b/runtime/cudaq/base_operator.h @@ -31,6 +31,6 @@ class base_operator { virtual std::string to_string() const = 0; /// @brief Return the degrees of freedom that the operator acts on. - virtual std::vector get_degrees() const = 0; + virtual std::vector degrees() const = 0; }; } From b7a3bfa17ce5da9dcb953d91c891a334eb37e6d9 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 10:37:39 -0800 Subject: [PATCH 007/311] adding base integrator and time stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 71 +++++++++++++++---------------- runtime/cudaq/base_time_stepper.h | 6 +-- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index a4dc982752..15c7c2f7e0 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -8,51 +8,48 @@ #pragma once +#include "base_time_stepper.h" +#include "operators.h" +#include "schedule.h" #include -#include #include -#include "schedule.h" -#include "operators.h" +#include namespace cudaq { template class BaseIntegrator { protected: - std::map integrator_options; - TState state; - double t; - std::map dimensions; - std::shared_ptr schedule; - std::shared_ptr hamiltonian; - std::shared_ptr> stepper; - std::vector> collapse_operators; + std::map integrator_options; + TState state; + double t; + std::map dimensions; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; + std::shared_ptr> stepper; + std::vector> collapse_operators; - virtual void post_init() = 0; + virtual void post_init() = 0; public: - virtual ~BaseIntegrator() = default; - - void set_state(const TState &initial_state, double t0 = 0.0) { - state = initial_state; - t = t0; - } - - void set_system( - const std::map &dimensions, - std::shared_ptr schedule, - std::shared_ptr hamiltonian, - std::vector> collapse_operators = {} - ) { - this->dimensions = dimensions; - this->schedule = schedule; - this->hamiltonian = hamiltonian; - this->collapse_operators = collapse_operators; - } - - virtual void integrate(double t) = 0; - - std::pair get_state() const { - return {t, state}; - } + virtual ~BaseIntegrator() = default; + + void set_state(const TState &initial_state, double t0 = 0.0) { + state = initial_state; + t = t0; + } + + void set_system( + const std::map &dimensions, std::shared_ptr schedule, + std::shared_ptr hamiltonian, + std::vector> collapse_operators = {}) { + this->dimensions = dimensions; + this->schedule = schedule; + this->hamiltonian = hamiltonian; + this->collapse_operators = collapse_operators; + } + + virtual void integrate(double t) = 0; + + std::pair get_state() const { return {t, state}; } }; -} +} // namespace cudaq diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index d92184825b..ce1502269e 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -12,8 +12,8 @@ namespace cudaq { template class BaseTimeStepper { public: - virtual ~BaseTimeStepper() = default; + virtual ~BaseTimeStepper() = default; - virtual void compute(TState &state, double t) = 0; + virtual void compute(TState &state, double t) = 0; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file From 6701ed8cabc391299b1270886d4ea8ee79fb446a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 20:33:12 -0800 Subject: [PATCH 008/311] * Adding base test cases for Runge-Kutta stepper and integrator * Removing base_operator class, as operator_sum will act as a base class Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 8 +- runtime/cudaq/base_operator.h | 36 ----- runtime/cudaq/base_time_stepper.h | 2 +- runtime/cudaq/runge_kutta_integrator.h | 46 ++++++ runtime/cudaq/runge_kutta_time_stepper.h | 33 ++++ unittests/CMakeLists.txt | 2 + unittests/dynamics/runge_kutta_test_helpers.h | 24 +++ .../dynamics/test_runge_kutta_integrator.cpp | 132 ++++++++++++++++ .../test_runge_kutta_time_stepper.cpp | 148 ++++++++++++++++++ 9 files changed, 390 insertions(+), 41 deletions(-) delete mode 100644 runtime/cudaq/base_operator.h create mode 100644 runtime/cudaq/runge_kutta_integrator.h create mode 100644 runtime/cudaq/runge_kutta_time_stepper.h create mode 100644 unittests/dynamics/runge_kutta_test_helpers.h create mode 100644 unittests/dynamics/test_runge_kutta_integrator.cpp create mode 100644 unittests/dynamics/test_runge_kutta_time_stepper.cpp diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 15c7c2f7e0..e3196bd52d 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -24,9 +24,9 @@ class BaseIntegrator { double t; std::map dimensions; std::shared_ptr schedule; - std::shared_ptr hamiltonian; + std::shared_ptr hamiltonian; std::shared_ptr> stepper; - std::vector> collapse_operators; + std::vector> collapse_operators; virtual void post_init() = 0; @@ -40,8 +40,8 @@ class BaseIntegrator { void set_system( const std::map &dimensions, std::shared_ptr schedule, - std::shared_ptr hamiltonian, - std::vector> collapse_operators = {}) { + std::shared_ptr hamiltonian, + std::vector> collapse_operators = {}) { this->dimensions = dimensions; this->schedule = schedule; this->hamiltonian = hamiltonian; diff --git a/runtime/cudaq/base_operator.h b/runtime/cudaq/base_operator.h deleted file mode 100644 index ea3475a5ee..0000000000 --- a/runtime/cudaq/base_operator.h +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "cudaq/matrix.h" -#include "cudaq/utils/tensor.h" -#include -#include -#include -#include - -namespace cudaq { -/// @brief Base class for all operator types. -class base_operator { -public: - virtual ~base_operator() = default; - - /// @brief Evaluate the operator with given parameters - virtual std::complex evaluate(const std::map> ¶meters) const = 0; - - /// @brief Convert the operator to a matrix representation. - virtual matrix_2 to_matrix(const std::map &dimensions, const std::map> ¶meters = {}) const = 0; - - /// @brief Convert the operator to a string representation. - virtual std::string to_string() const = 0; - - /// @brief Return the degrees of freedom that the operator acts on. - virtual std::vector degrees() const = 0; -}; -} diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index ce1502269e..4488de8b44 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -14,6 +14,6 @@ class BaseTimeStepper { public: virtual ~BaseTimeStepper() = default; - virtual void compute(TState &state, double t) = 0; + virtual void compute(TState &state, double t, double step_size) = 0; }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h new file mode 100644 index 0000000000..41d3da1715 --- /dev/null +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -0,0 +1,46 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "base_integrator.h" +#include "runge_kutta_time_stepper.h" +#include + +namespace cudaq { +template +class RungeKuttaIntegrator : public BaseIntegrator { +public: + using DerivativeFunction = std::function; + + explicit RungeKuttaIntegrator(DerivativeFunction f) : stepper(std::make_shared>(f)) {} + + // Initializes the integrator + void post_init() override { + if (!this->stepper) { + throw std::runtime_error("Time stepper is not set"); + } + } + + // Advances the system's state from current time to `t` + void integrate(double target_t) override { + if (!this->schedule || !this->hamiltonian) { + throw std::runtime_error("System is not properly set!"); + } + + while (this->t < target_t) { + stepper->compute(this->state, this->t); + // Time step size + this->t += 0.01; + } + } + +private: + std::shared_ptr> stepper; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_time_stepper.h b/runtime/cudaq/runge_kutta_time_stepper.h new file mode 100644 index 0000000000..7718251a07 --- /dev/null +++ b/runtime/cudaq/runge_kutta_time_stepper.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "base_time_stepper.h" +#include + +namespace cudaq { +template +class RungeKuttaTimeStepper : public BaseTimeStepper { +public: + using DerivativeFunction = std::function; + + RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} + + void compute(TState &state, double t, double dt = 0.01) override { + // 4th order Runge-Kutta method + TState k1 = derivativeFunc(state, t); + TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); + TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); + TState k4 = derivativeFunc(state + dt * k3, t + dt); + + state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); + } + +private: + DerivativeFunction derivativeFunc; +}; +} \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index df4748f80d..ef98549244 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -49,6 +49,8 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/elementary_ops_simple.cpp dynamics/elementary_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp + dynamics/test_runge_kutta_time_stepper.cpp + dynamics/test_runge_kutta_integrator.cpp ) # Make it so we can get function symbols diff --git a/unittests/dynamics/runge_kutta_test_helpers.h b/unittests/dynamics/runge_kutta_test_helpers.h new file mode 100644 index 0000000000..28ec5c7723 --- /dev/null +++ b/unittests/dynamics/runge_kutta_test_helpers.h @@ -0,0 +1,24 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include + +// A simple state type +using TestState = double; + +// Simple derivative function: dx/dt = -x (exponential decay) +inline TestState simple_derivative(const TestState &state, double t) { + return -state; +} + +// A complex function: dx/dt = sin(t) +inline TestState sine_derivative(const TestState &state, double t) { + return std::sin(t); +} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp new file mode 100644 index 0000000000..431ea1457a --- /dev/null +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -0,0 +1,132 @@ +// /******************************************************************************* +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * +// ******************************************************************************/ + +#include "runge_kutta_test_helpers.h" +#include "cudaq/runge_kutta_integrator.h" +#include +#include +#include + +using namespace cudaq; + +// Test fixture class +class RungeKuttaIntegratorTest : public ::testing::Test { +protected: + RungeKuttaIntegrator *integrator; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; + + void SetUp() override { + integrator = new RungeKuttaIntegrator(simple_derivative); + // Initial state and time + integrator->set_state(1.0, 0.0); + + // A simple step sequence for the schedule + std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; + + // Dummy parameters + std::vector parameters = {"param1"}; + + // A simple parameter function + auto value_function = [](const std::string ¶m, const std::complex &step) { + return step; + }; + + // A valid schedule instance + schedule = std::make_shared(steps, parameters, value_function); + + // A simple hamiltonian as an operator_sum + hamiltonian = std::make_shared(); + *hamiltonian += 0.5 * elementary_operator::identity(0); + *hamiltonian += 0.5 * elementary_operator::number(0); + + // System with valid components + integrator->set_system({{0, 2}}, schedule, hamiltonian); + } + + void TearDown() override { + delete integrator; + } +}; + +// Basic integration +TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { + integrator->integrate(1.0); + + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Basic Runge-Kutta integration failed!"; +} + +// Time evolution +TEST_F(RungeKuttaIntegratorTest, TimeEvolution) { + integrator->integrate(2.0); + + double expected = 2.0; + + EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) << "Integrator did not correctly update time!"; +} + +// Large step size +TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { + integrator->integrate(5.0); + + double expected = std::exp(-5.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) << "Runge-Kutta integration failed for large step size!!"; +} + + +// // Integrating Sine function +// TEST_F(RungeKuttaIntegratorTest, SineFunction) { +// integrator = new RungeKuttaIntegrator(sine_derivative); +// integrator->set_state(1.0, 0.0); +// integrator->set_system({{0, 2}}, schedule, hamiltonian); + +// integrator->integrate(M_PI / 2); + +// double expected = std::cos(M_PI / 2); + +// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Runge-Kutta integration for sine function failed!"; +// } + + +// Small step size +TEST_F(RungeKuttaIntegratorTest, SmallStepIntegration) { + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); + + double step_size = 0.001; + while (integrator->get_state().first < 1.0) { + integrator->integrate(integrator->get_state().first + step_size); + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) << "Runge-Kutta integration for small step size failed!"; +} + + +// Large step size +TEST_F(RungeKuttaIntegratorTest, LargeStepIntegration) { + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); + + double step_size = 0.5; + double t = 0.0; + double target_t = 1.0; + while (t < target_t) { + integrator->integrate(std::min(t + step_size, target_t)); + t += step_size; + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) << "Runge-Kutta integration for large step size failed!"; +} diff --git a/unittests/dynamics/test_runge_kutta_time_stepper.cpp b/unittests/dynamics/test_runge_kutta_time_stepper.cpp new file mode 100644 index 0000000000..bc32bb587c --- /dev/null +++ b/unittests/dynamics/test_runge_kutta_time_stepper.cpp @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "runge_kutta_test_helpers.h" +#include "cudaq/runge_kutta_time_stepper.h" +#include +#include +#include + +// Test fixture class +class RungeKuttaTimeStepperTest : public ::testing::Test { +protected: + std::shared_ptr> stepper; + + void SetUp() override { + stepper = std::make_shared>(simple_derivative); + } +}; + +// Single step integration +TEST_F(RungeKuttaTimeStepperTest, SingleStep) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; + + stepper->compute(state, t, dt); + + // Expected result using analytical solution: x(t) = e^(-t) + double expected = std::exp(-dt); + + EXPECT_NEAR(state, expected, 1e-3) << "Single step Runge-Kutta integration failed!"; +} + +// Multiple step integration +TEST_F(RungeKuttaTimeStepperTest, MultipleSteps) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; + + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } + + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-2) << "Multiple step Runge-Kutta integration failed!"; +} + +// Convergence to Analytical Solution +TEST_F(RungeKuttaTimeStepperTest, Convergence) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.01; + int steps = 100; + + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta integration does not converge!"; +} + +// // Integrating Sine function +// TEST_F(RungeKuttaTimeStepperTest, SineFunction) { +// auto sine_stepper = std::make_shared>(sine_derivative); + +// // Initial values +// double state = 0.0; +// double t = 0.0; +// double dt = 0.1; +// int steps = 10; + +// for (int i = 0; i < steps; i++) { +// sine_stepper->compute(state, t, dt); +// } + +// // Expected integral of sin(t) over [0, 1] is 1 - cos(1) +// double expected = 1 - std::cos(1); + +// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine function failed!"; +// } + +// Handling small steps sizes +TEST_F(RungeKuttaTimeStepperTest, SmallStepSize) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1e-5; + int steps = 100000; + + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta fails with small step sizes!"; +} + +// Handling large steps sizes +TEST_F(RungeKuttaTimeStepperTest, LargeStepSize) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1.0; + + stepper->compute(state, t, dt); + + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-1) << "Runge-Kutta is unstable with large step sizes!"; +} + +// Constant derivative (dx/dt = 0) +TEST_F(RungeKuttaTimeStepperTest, ConstantFunction) { + auto constant_stepper = std::make_shared>( + [](const TestState &state, double t) { + return 0.0; + } + ); + + // Initial values + double state = 5.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; + + for (int i = 0; i < steps; i++) { + constant_stepper->compute(state, t, dt); + } + + EXPECT_NEAR(state, 5.0, 1e-6) << "Runge-Kutta should not change a constant function!"; +} + + + From e0aa4fe4a1ffc271664c02e654e56646846cc148 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 15:12:20 -0800 Subject: [PATCH 009/311] * Adding helpers functionlity * Aggregating parameters * Extracting documentation * Extracting positional and keyword arguments * Generating all quantum states for given degrees and dimensions * Permuting a given matrix * Canonicalizing degrees * Adding test cases for the above functions * Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 4 +- runtime/cudaq/dynamics/helpers.cpp | 135 +++++++++++++ runtime/cudaq/helpers.h | 42 ++++ runtime/cudaq/runge_kutta_integrator.h | 41 ++-- runtime/cudaq/runge_kutta_time_stepper.h | 24 +-- unittests/CMakeLists.txt | 1 + unittests/dynamics/runge_kutta_test_helpers.h | 4 +- unittests/dynamics/test_helpers.cpp | 189 ++++++++++++++++++ .../dynamics/test_runge_kutta_integrator.cpp | 134 ++++++------- .../test_runge_kutta_time_stepper.cpp | 152 +++++++------- 10 files changed, 549 insertions(+), 177 deletions(-) create mode 100644 runtime/cudaq/dynamics/helpers.cpp create mode 100644 runtime/cudaq/helpers.h create mode 100644 unittests/dynamics/test_helpers.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 9709cd9a71..d608aba2c3 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -1,5 +1,5 @@ # ============================================================================ # -# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # # All rights reserved. # # # # This source code and the accompanying materials are made available under # @@ -11,7 +11,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp ) add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp new file mode 100644 index 0000000000..ad86a2c26b --- /dev/null +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/helpers.h" +#include +#include + +namespace cudaq { +// Aggregate parameters from multiple mappings. +std::map OperatorHelpers::aggregate_parameters(const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; + } + } + } + + return parameter_descriptions; +} + +// Extract documentation for a specific parameter from docstring. +std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } + + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + R"(\s*(\(.*\))?:\s*(.*)$)", std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if(std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + } + } + } catch (...) { + return ""; + } + + return ""; +} + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> OperatorHelpers::args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); + } + } + + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); + } + } + + return {extracted_args, kwonly_dict}; +} + +// Generate all possible quantum states for given degrees and dimensions +std::vector OperatorHelpers::generate_all_states(const std::vector °rees, const std::map &dimensions) { + if (degrees.empty()) { + return {}; + } + + std::vector> states; + for (int state = 0; state < dimensions.at(degrees[0]); state++) { + states.push_back({std::to_string(state)}); + } + + for (size_t i = 1; i < degrees.size(); i++) { + std::vector> new_states; + for (const auto ¤t : states) { + for (int state = 0; state < dimensions.at(degrees[i]); state++) { + auto new_entry = current; + new_entry.push_back(std::to_string(state)); + new_states.push_back(new_entry); + } + } + states = new_states; + } + + std::vector result; + for (const auto &state : states) { + std::ostringstream joined; + for (const auto &s : state) { + joined << s; + } + result.push_back(joined.str()); + } + return result; +} + +// Permute a given eigen matrix +void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation) { + Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); + + for (size_t i = 0; i < permutation.size(); i++) { + for (size_t j = 0; j < permutation.size(); j++) { + permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); + } + } + + matrix = permuted_matrix; +} + +// Canonicalize degrees by sorting in descending order +std::vector OperatorHelpers::canonicalize_degrees(const std::vector °rees) { + std::vector sorted_degrees = degrees; + std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); + return sorted_degrees; +} +} \ No newline at end of file diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h new file mode 100644 index 0000000000..488ff3bf0c --- /dev/null +++ b/runtime/cudaq/helpers.h @@ -0,0 +1,42 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cudaq { +class OperatorHelpers { +public: + // Aggregate parameters from multiple mappings. + static std::map aggregate_parameters(const std::vector> ¶meter_mappings); + + // Extract documentation for a specific parameter from docstring. + static std::string parameter_docs(const std::string ¶m_name, const std::string &docs); + + // Extract positional arguments and keyword-only arguments. + static std::pair, std::map> args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, const std::vector &kwonly_args); + + // Generate all possible quantum states for given degrees and dimensions. + static std::vector generate_all_states(const std::vector °rees, const std::map &dimensions); + + // Permute a given Eigen matrix. + static void permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation); + + // Canonicalize degrees by sorting in descending order. + static std::vector canonicalize_degrees(const std::vector °rees); +}; +} \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 41d3da1715..9914258386 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -16,31 +16,32 @@ namespace cudaq { template class RungeKuttaIntegrator : public BaseIntegrator { public: - using DerivativeFunction = std::function; + using DerivativeFunction = std::function; - explicit RungeKuttaIntegrator(DerivativeFunction f) : stepper(std::make_shared>(f)) {} + explicit RungeKuttaIntegrator(DerivativeFunction f) + : stepper(std::make_shared>(f)) {} - // Initializes the integrator - void post_init() override { - if (!this->stepper) { - throw std::runtime_error("Time stepper is not set"); - } + // Initializes the integrator + void post_init() override { + if (!this->stepper) { + throw std::runtime_error("Time stepper is not set"); } + } - // Advances the system's state from current time to `t` - void integrate(double target_t) override { - if (!this->schedule || !this->hamiltonian) { - throw std::runtime_error("System is not properly set!"); - } - - while (this->t < target_t) { - stepper->compute(this->state, this->t); - // Time step size - this->t += 0.01; - } + // Advances the system's state from current time to `t` + void integrate(double target_t) override { + if (!this->schedule || !this->hamiltonian) { + throw std::runtime_error("System is not properly set!"); } + while (this->t < target_t) { + stepper->compute(this->state, this->t); + // Time step size + this->t += 0.01; + } + } + private: - std::shared_ptr> stepper; + std::shared_ptr> stepper; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_time_stepper.h b/runtime/cudaq/runge_kutta_time_stepper.h index 7718251a07..1dcd1f69cc 100644 --- a/runtime/cudaq/runge_kutta_time_stepper.h +++ b/runtime/cudaq/runge_kutta_time_stepper.h @@ -13,21 +13,21 @@ namespace cudaq { template class RungeKuttaTimeStepper : public BaseTimeStepper { public: - using DerivativeFunction = std::function; + using DerivativeFunction = std::function; - RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} + RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} - void compute(TState &state, double t, double dt = 0.01) override { - // 4th order Runge-Kutta method - TState k1 = derivativeFunc(state, t); - TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); - TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); - TState k4 = derivativeFunc(state + dt * k3, t + dt); + void compute(TState &state, double t, double dt = 0.01) override { + // 4th order Runge-Kutta method + TState k1 = derivativeFunc(state, t); + TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); + TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); + TState k4 = derivativeFunc(state + dt * k3, t + dt); - state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); - } + state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); + } private: - DerivativeFunction derivativeFunc; + DerivativeFunction derivativeFunc; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ef98549244..f788ea0c48 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -51,6 +51,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/product_operators_arithmetic.cpp dynamics/test_runge_kutta_time_stepper.cpp dynamics/test_runge_kutta_integrator.cpp + dynamics/test_helpers.cpp ) # Make it so we can get function symbols diff --git a/unittests/dynamics/runge_kutta_test_helpers.h b/unittests/dynamics/runge_kutta_test_helpers.h index 28ec5c7723..4f93ffa242 100644 --- a/unittests/dynamics/runge_kutta_test_helpers.h +++ b/unittests/dynamics/runge_kutta_test_helpers.h @@ -15,10 +15,10 @@ using TestState = double; // Simple derivative function: dx/dt = -x (exponential decay) inline TestState simple_derivative(const TestState &state, double t) { - return -state; + return -state; } // A complex function: dx/dt = sin(t) inline TestState sine_derivative(const TestState &state, double t) { - return std::sin(t); + return std::sin(t); } diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp new file mode 100644 index 0000000000..9e471a29bd --- /dev/null +++ b/unittests/dynamics/test_helpers.cpp @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/helpers.h" +#include +#include + +using namespace cudaq; + +TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { + std::vector> mappings = { + {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}} + }; + + auto result = OperatorHelpers::aggregate_parameters(mappings); + + EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); + EXPECT_EQ(result["beta"], "Parameter B"); + EXPECT_EQ(result["gamma"], "New Parameter"); +} + +TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { + std::vector> mappings; + auto result = OperatorHelpers::aggregate_parameters(mappings); + + EXPECT_TRUE(result.empty()); +} + +TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { + std::string docstring = + "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; + + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, "The first parameter."); + + result = OperatorHelpers::parameter_docs("beta", docstring); + EXPECT_EQ(result, "The second parameter."); +} + +TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { + std::string docstring = + "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; + + auto result = OperatorHelpers::parameter_docs("gamma", docstring); + EXPECT_EQ(result, ""); +} + +TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { + std::string docstring = ""; + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, ""); +} + +TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { + std::vector degrees = {0, 1}; + std::map dimensions = {{0, 2}, {1, 2}}; + + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"00", "01", "10", "11"}; + + EXPECT_EQ(states, expected_states); +} + +TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"000", "001", "010", "011", "100", "101", "110", "111"}; + + EXPECT_EQ(states, expected_states); +} + +TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { + std::vector degrees; + std::map dimensions; + + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + EXPECT_TRUE(states.empty()); +} + +TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}}; + + EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), std::out_of_range); +} + +TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { + Eigen::MatrixXcd matrix(2, 2); + matrix << 1, 2, + 3, 4; + + // Swap rows and columns + std::vector permutation = {1, 0}; + + OperatorHelpers::permute_matrix(matrix, permutation); + + Eigen::MatrixXcd expected(2, 2); + expected << 4, 3, + 2, 1; + + EXPECT_EQ(matrix, expected); +} + +TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { + Eigen::MatrixXcd matrix(3, 3); + matrix << 1, 2, 3, + 4, 5, 6, + 7, 8, 9; + + // No change + std::vector permutation = {0, 1, 2}; + + OperatorHelpers::permute_matrix(matrix, permutation); + + Eigen::MatrixXcd expected(3, 3); + expected << 1, 2, 3, + 4, 5, 6, + 7, 8, 9; + + EXPECT_EQ(matrix, expected); +} + +TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { + std::vector degrees = {3, 1, 2}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{3, 2, 1})); +} + +TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { + std::vector degrees = {5, 4, 3, 2, 1}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); +} + +TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { + std::vector degrees; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_TRUE(sorted.empty()); +} + +TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { + std::map kwargs = { + {"alpha", "0.5"}, + {"beta", "1.0"}, + {"gamma", "2.0"} + }; + + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; + + auto [args, kwonly] = OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); + + EXPECT_EQ(args.size(), 2); + EXPECT_EQ(args[0], "0.5"); + EXPECT_EQ(args[1], "1.0"); + + EXPECT_EQ(kwonly.size(), 1); + EXPECT_EQ(kwonly["gamma"], "2.0"); +} + + +TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { + std::map kwargs = { + {"beta", "1.0"}, + {"gamma", "2.0"} + }; + + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; + + EXPECT_THROW(OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), std::invalid_argument); +} + + + + diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 431ea1457a..c75a7c8d6d 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -1,88 +1,87 @@ // /******************************************************************************* -// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * -// * All rights reserved. * -// * * -// * This source code and the accompanying materials are made available under * -// * the terms of the Apache License 2.0 which accompanies this distribution. * +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * // ******************************************************************************/ -#include "runge_kutta_test_helpers.h" #include "cudaq/runge_kutta_integrator.h" +#include "runge_kutta_test_helpers.h" +#include #include #include -#include using namespace cudaq; // Test fixture class class RungeKuttaIntegratorTest : public ::testing::Test { protected: - RungeKuttaIntegrator *integrator; - std::shared_ptr schedule; - std::shared_ptr hamiltonian; + RungeKuttaIntegrator *integrator; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; - void SetUp() override { - integrator = new RungeKuttaIntegrator(simple_derivative); - // Initial state and time - integrator->set_state(1.0, 0.0); + void SetUp() override { + integrator = new RungeKuttaIntegrator(simple_derivative); + // Initial state and time + integrator->set_state(1.0, 0.0); - // A simple step sequence for the schedule - std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; + // A simple step sequence for the schedule + std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; - // Dummy parameters - std::vector parameters = {"param1"}; + // Dummy parameters + std::vector parameters = {"param1"}; - // A simple parameter function - auto value_function = [](const std::string ¶m, const std::complex &step) { - return step; - }; + // A simple parameter function + auto value_function = [](const std::string ¶m, + const std::complex &step) { return step; }; - // A valid schedule instance - schedule = std::make_shared(steps, parameters, value_function); + // A valid schedule instance + schedule = std::make_shared(steps, parameters, value_function); - // A simple hamiltonian as an operator_sum - hamiltonian = std::make_shared(); - *hamiltonian += 0.5 * elementary_operator::identity(0); - *hamiltonian += 0.5 * elementary_operator::number(0); + // A simple hamiltonian as an operator_sum + hamiltonian = std::make_shared(); + *hamiltonian += 0.5 * elementary_operator::identity(0); + *hamiltonian += 0.5 * elementary_operator::number(0); - // System with valid components - integrator->set_system({{0, 2}}, schedule, hamiltonian); - } + // System with valid components + integrator->set_system({{0, 2}}, schedule, hamiltonian); + } - void TearDown() override { - delete integrator; - } + void TearDown() override { delete integrator; } }; // Basic integration TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { - integrator->integrate(1.0); + integrator->integrate(1.0); - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); - EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Basic Runge-Kutta integration failed!"; + EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) + << "Basic Runge-Kutta integration failed!"; } // Time evolution TEST_F(RungeKuttaIntegratorTest, TimeEvolution) { - integrator->integrate(2.0); + integrator->integrate(2.0); - double expected = 2.0; + double expected = 2.0; - EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) << "Integrator did not correctly update time!"; + EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) + << "Integrator did not correctly update time!"; } // Large step size TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { - integrator->integrate(5.0); + integrator->integrate(5.0); - double expected = std::exp(-5.0); + double expected = std::exp(-5.0); - EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) << "Runge-Kutta integration failed for large step size!!"; + EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) + << "Runge-Kutta integration failed for large step size!!"; } - // // Integrating Sine function // TEST_F(RungeKuttaIntegratorTest, SineFunction) { // integrator = new RungeKuttaIntegrator(sine_derivative); @@ -93,40 +92,41 @@ TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { // double expected = std::cos(M_PI / 2); -// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Runge-Kutta integration for sine function failed!"; +// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << +// "Runge-Kutta integration for sine function failed!"; // } - // Small step size TEST_F(RungeKuttaIntegratorTest, SmallStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); - double step_size = 0.001; - while (integrator->get_state().first < 1.0) { - integrator->integrate(integrator->get_state().first + step_size); - } + double step_size = 0.001; + while (integrator->get_state().first < 1.0) { + integrator->integrate(integrator->get_state().first + step_size); + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) << "Runge-Kutta integration for small step size failed!"; + EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) + << "Runge-Kutta integration for small step size failed!"; } - // Large step size TEST_F(RungeKuttaIntegratorTest, LargeStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); - double step_size = 0.5; - double t = 0.0; - double target_t = 1.0; - while (t < target_t) { - integrator->integrate(std::min(t + step_size, target_t)); - t += step_size; - } + double step_size = 0.5; + double t = 0.0; + double target_t = 1.0; + while (t < target_t) { + integrator->integrate(std::min(t + step_size, target_t)); + t += step_size; + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) << "Runge-Kutta integration for large step size failed!"; + EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) + << "Runge-Kutta integration for large step size failed!"; } diff --git a/unittests/dynamics/test_runge_kutta_time_stepper.cpp b/unittests/dynamics/test_runge_kutta_time_stepper.cpp index bc32bb587c..4c4c7b7588 100644 --- a/unittests/dynamics/test_runge_kutta_time_stepper.cpp +++ b/unittests/dynamics/test_runge_kutta_time_stepper.cpp @@ -6,75 +6,80 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "runge_kutta_test_helpers.h" #include "cudaq/runge_kutta_time_stepper.h" +#include "runge_kutta_test_helpers.h" +#include #include #include -#include // Test fixture class class RungeKuttaTimeStepperTest : public ::testing::Test { protected: - std::shared_ptr> stepper; + std::shared_ptr> stepper; - void SetUp() override { - stepper = std::make_shared>(simple_derivative); - } + void SetUp() override { + stepper = std::make_shared>( + simple_derivative); + } }; // Single step integration TEST_F(RungeKuttaTimeStepperTest, SingleStep) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; - stepper->compute(state, t, dt); + stepper->compute(state, t, dt); - // Expected result using analytical solution: x(t) = e^(-t) - double expected = std::exp(-dt); + // Expected result using analytical solution: x(t) = e^(-t) + double expected = std::exp(-dt); - EXPECT_NEAR(state, expected, 1e-3) << "Single step Runge-Kutta integration failed!"; + EXPECT_NEAR(state, expected, 1e-3) + << "Single step Runge-Kutta integration failed!"; } // Multiple step integration TEST_F(RungeKuttaTimeStepperTest, MultipleSteps) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-2) << "Multiple step Runge-Kutta integration failed!"; + EXPECT_NEAR(state, expected, 1e-2) + << "Multiple step Runge-Kutta integration failed!"; } // Convergence to Analytical Solution TEST_F(RungeKuttaTimeStepperTest, Convergence) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.01; - int steps = 100; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.01; + int steps = 100; - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta integration does not converge!"; + EXPECT_NEAR(state, expected, 1e-3) + << "Runge-Kutta integration does not converge!"; } // // Integrating Sine function // TEST_F(RungeKuttaTimeStepperTest, SineFunction) { -// auto sine_stepper = std::make_shared>(sine_derivative); +// auto sine_stepper = +// std::make_shared>(sine_derivative); // // Initial values // double state = 0.0; @@ -89,60 +94,59 @@ TEST_F(RungeKuttaTimeStepperTest, Convergence) { // // Expected integral of sin(t) over [0, 1] is 1 - cos(1) // double expected = 1 - std::cos(1); -// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine function failed!"; +// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine +// function failed!"; // } // Handling small steps sizes TEST_F(RungeKuttaTimeStepperTest, SmallStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1e-5; - int steps = 100000; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1e-5; + int steps = 100000; - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta fails with small step sizes!"; + EXPECT_NEAR(state, expected, 1e-3) + << "Runge-Kutta fails with small step sizes!"; } // Handling large steps sizes TEST_F(RungeKuttaTimeStepperTest, LargeStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1.0; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1.0; - stepper->compute(state, t, dt); + stepper->compute(state, t, dt); - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-1) << "Runge-Kutta is unstable with large step sizes!"; + EXPECT_NEAR(state, expected, 1e-1) + << "Runge-Kutta is unstable with large step sizes!"; } // Constant derivative (dx/dt = 0) TEST_F(RungeKuttaTimeStepperTest, ConstantFunction) { - auto constant_stepper = std::make_shared>( - [](const TestState &state, double t) { - return 0.0; - } - ); - - // Initial values - double state = 5.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; - - for (int i = 0; i < steps; i++) { - constant_stepper->compute(state, t, dt); - } - - EXPECT_NEAR(state, 5.0, 1e-6) << "Runge-Kutta should not change a constant function!"; + auto constant_stepper = + std::make_shared>( + [](const TestState &state, double t) { return 0.0; }); + + // Initial values + double state = 5.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; + + for (int i = 0; i < steps; i++) { + constant_stepper->compute(state, t, dt); + } + + EXPECT_NEAR(state, 5.0, 1e-6) + << "Runge-Kutta should not change a constant function!"; } - - - From 1fd530e83dea18d48bbf60a6d1fab7b22af454cb Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 15:18:55 -0800 Subject: [PATCH 010/311] Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 198 +++++++++++++++------------- runtime/cudaq/helpers.h | 56 ++++---- unittests/dynamics/test_helpers.cpp | 193 +++++++++++++-------------- 3 files changed, 227 insertions(+), 220 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index ad86a2c26b..e6c50d14a0 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,129 +7,141 @@ ******************************************************************************/ #include "cudaq/helpers.h" -#include #include +#include namespace cudaq { // Aggregate parameters from multiple mappings. -std::map OperatorHelpers::aggregate_parameters(const std::vector> ¶meter_mappings) { - std::map parameter_descriptions; - - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; - } - } +std::map OperatorHelpers::aggregate_parameters( + const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; + } } + } - return parameter_descriptions; + return parameter_descriptions; } // Extract documentation for a specific parameter from docstring. -std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; - } - - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + R"(\s*(\(.*\))?:\s*(.*)$)", std::regex::multiline); - - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; - - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if(std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); - } - } - } catch (...) { - return ""; +std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, + const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } + + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", + std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if (std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + } } - + } catch (...) { return ""; + } + + return ""; } // Extract positional arguments and keyword-only arguments. -std::pair, std::map> OperatorHelpers::args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); - } +std::pair, std::map> +OperatorHelpers::args_from_kwargs( + const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); } + } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); - } + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); } + } - return {extracted_args, kwonly_dict}; + return {extracted_args, kwonly_dict}; } // Generate all possible quantum states for given degrees and dimensions -std::vector OperatorHelpers::generate_all_states(const std::vector °rees, const std::map &dimensions) { - if (degrees.empty()) { - return {}; - } - - std::vector> states; - for (int state = 0; state < dimensions.at(degrees[0]); state++) { - states.push_back({std::to_string(state)}); +std::vector +OperatorHelpers::generate_all_states(const std::vector °rees, + const std::map &dimensions) { + if (degrees.empty()) { + return {}; + } + + std::vector> states; + for (int state = 0; state < dimensions.at(degrees[0]); state++) { + states.push_back({std::to_string(state)}); + } + + for (size_t i = 1; i < degrees.size(); i++) { + std::vector> new_states; + for (const auto ¤t : states) { + for (int state = 0; state < dimensions.at(degrees[i]); state++) { + auto new_entry = current; + new_entry.push_back(std::to_string(state)); + new_states.push_back(new_entry); + } } - - for (size_t i = 1; i < degrees.size(); i++) { - std::vector> new_states; - for (const auto ¤t : states) { - for (int state = 0; state < dimensions.at(degrees[i]); state++) { - auto new_entry = current; - new_entry.push_back(std::to_string(state)); - new_states.push_back(new_entry); - } - } - states = new_states; - } - - std::vector result; - for (const auto &state : states) { - std::ostringstream joined; - for (const auto &s : state) { - joined << s; - } - result.push_back(joined.str()); + states = new_states; + } + + std::vector result; + for (const auto &state : states) { + std::ostringstream joined; + for (const auto &s : state) { + joined << s; } - return result; + result.push_back(joined.str()); + } + return result; } // Permute a given eigen matrix -void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation) { - Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); +void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, + const std::vector &permutation) { + Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); - for (size_t i = 0; i < permutation.size(); i++) { - for (size_t j = 0; j < permutation.size(); j++) { - permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); - } + for (size_t i = 0; i < permutation.size(); i++) { + for (size_t j = 0; j < permutation.size(); j++) { + permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); } + } - matrix = permuted_matrix; + matrix = permuted_matrix; } // Canonicalize degrees by sorting in descending order -std::vector OperatorHelpers::canonicalize_degrees(const std::vector °rees) { - std::vector sorted_degrees = degrees; - std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); - return sorted_degrees; +std::vector +OperatorHelpers::canonicalize_degrees(const std::vector °rees) { + std::vector sorted_degrees = degrees; + std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); + return sorted_degrees; } -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 488ff3bf0c..52bb131d0c 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -8,35 +8,43 @@ #pragma once -#include -#include -#include +#include #include #include -#include +#include #include -#include +#include +#include +#include namespace cudaq { class OperatorHelpers { public: - // Aggregate parameters from multiple mappings. - static std::map aggregate_parameters(const std::vector> ¶meter_mappings); - - // Extract documentation for a specific parameter from docstring. - static std::string parameter_docs(const std::string ¶m_name, const std::string &docs); - - // Extract positional arguments and keyword-only arguments. - static std::pair, std::map> args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, const std::vector &kwonly_args); - - // Generate all possible quantum states for given degrees and dimensions. - static std::vector generate_all_states(const std::vector °rees, const std::map &dimensions); - - // Permute a given Eigen matrix. - static void permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation); - - // Canonicalize degrees by sorting in descending order. - static std::vector canonicalize_degrees(const std::vector °rees); + // Aggregate parameters from multiple mappings. + static std::map + aggregate_parameters(const std::vector> + ¶meter_mappings); + + // Extract documentation for a specific parameter from docstring. + static std::string parameter_docs(const std::string ¶m_name, + const std::string &docs); + + // Extract positional arguments and keyword-only arguments. + static std::pair, std::map> + args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args); + + // Generate all possible quantum states for given degrees and dimensions. + static std::vector + generate_all_states(const std::vector °rees, + const std::map &dimensions); + + // Permute a given Eigen matrix. + static void permute_matrix(Eigen::MatrixXcd &matrix, + const std::vector &permutation); + + // Canonicalize degrees by sorting in descending order. + static std::vector canonicalize_degrees(const std::vector °rees); }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp index 9e471a29bd..aa14cb5c10 100644 --- a/unittests/dynamics/test_helpers.cpp +++ b/unittests/dynamics/test_helpers.cpp @@ -13,177 +13,164 @@ using namespace cudaq; TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { - std::vector> mappings = { - {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}} - }; + std::vector> mappings = { + {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, + {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}}}; - auto result = OperatorHelpers::aggregate_parameters(mappings); + auto result = OperatorHelpers::aggregate_parameters(mappings); - EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); - EXPECT_EQ(result["beta"], "Parameter B"); - EXPECT_EQ(result["gamma"], "New Parameter"); + EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); + EXPECT_EQ(result["beta"], "Parameter B"); + EXPECT_EQ(result["gamma"], "New Parameter"); } TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { - std::vector> mappings; - auto result = OperatorHelpers::aggregate_parameters(mappings); + std::vector> mappings; + auto result = OperatorHelpers::aggregate_parameters(mappings); - EXPECT_TRUE(result.empty()); + EXPECT_TRUE(result.empty()); } TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { - std::string docstring = - "Description of function.\n" - "Arguments:\n" - " alpha (float): The first parameter.\n" - " beta (int): The second parameter."; + std::string docstring = "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); - EXPECT_EQ(result, "The first parameter."); + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, "The first parameter."); - result = OperatorHelpers::parameter_docs("beta", docstring); - EXPECT_EQ(result, "The second parameter."); + result = OperatorHelpers::parameter_docs("beta", docstring); + EXPECT_EQ(result, "The second parameter."); } TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { - std::string docstring = - "Description of function.\n" - "Arguments:\n" - " alpha (float): The first parameter.\n" - " beta (int): The second parameter."; + std::string docstring = "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("gamma", docstring); - EXPECT_EQ(result, ""); + auto result = OperatorHelpers::parameter_docs("gamma", docstring); + EXPECT_EQ(result, ""); } TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { - std::string docstring = ""; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); - EXPECT_EQ(result, ""); + std::string docstring = ""; + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, ""); } TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { - std::vector degrees = {0, 1}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::vector degrees = {0, 1}; + std::map dimensions = {{0, 2}, {1, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); - std::vector expected_states = {"00", "01", "10", "11"}; + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"00", "01", "10", "11"}; - EXPECT_EQ(states, expected_states); + EXPECT_EQ(states, expected_states); } TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { - std::vector degrees = {0, 1, 2}; - std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); - std::vector expected_states = {"000", "001", "010", "011", "100", "101", "110", "111"}; + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"000", "001", "010", "011", + "100", "101", "110", "111"}; - EXPECT_EQ(states, expected_states); + EXPECT_EQ(states, expected_states); } TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { - std::vector degrees; - std::map dimensions; + std::vector degrees; + std::map dimensions; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); - EXPECT_TRUE(states.empty()); + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + EXPECT_TRUE(states.empty()); } TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { - std::vector degrees = {0, 1, 2}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}}; - EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), std::out_of_range); + EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), + std::out_of_range); } TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { - Eigen::MatrixXcd matrix(2, 2); - matrix << 1, 2, - 3, 4; + Eigen::MatrixXcd matrix(2, 2); + matrix << 1, 2, 3, 4; - // Swap rows and columns - std::vector permutation = {1, 0}; + // Swap rows and columns + std::vector permutation = {1, 0}; - OperatorHelpers::permute_matrix(matrix, permutation); + OperatorHelpers::permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(2, 2); - expected << 4, 3, - 2, 1; + Eigen::MatrixXcd expected(2, 2); + expected << 4, 3, 2, 1; - EXPECT_EQ(matrix, expected); + EXPECT_EQ(matrix, expected); } TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { - Eigen::MatrixXcd matrix(3, 3); - matrix << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; + Eigen::MatrixXcd matrix(3, 3); + matrix << 1, 2, 3, 4, 5, 6, 7, 8, 9; - // No change - std::vector permutation = {0, 1, 2}; + // No change + std::vector permutation = {0, 1, 2}; - OperatorHelpers::permute_matrix(matrix, permutation); + OperatorHelpers::permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(3, 3); - expected << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; + Eigen::MatrixXcd expected(3, 3); + expected << 1, 2, 3, 4, 5, 6, 7, 8, 9; - EXPECT_EQ(matrix, expected); + EXPECT_EQ(matrix, expected); } TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { - std::vector degrees = {3, 1, 2}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); - EXPECT_EQ(sorted, (std::vector{3, 2, 1})); + std::vector degrees = {3, 1, 2}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { - std::vector degrees = {5, 4, 3, 2, 1}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); - EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); + std::vector degrees = {5, 4, 3, 2, 1}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { - std::vector degrees; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); - EXPECT_TRUE(sorted.empty()); + std::vector degrees; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_TRUE(sorted.empty()); } TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { - std::map kwargs = { - {"alpha", "0.5"}, - {"beta", "1.0"}, - {"gamma", "2.0"} - }; + std::map kwargs = { + {"alpha", "0.5"}, {"beta", "1.0"}, {"gamma", "2.0"}}; - std::vector required_args = {"alpha", "beta"}; - std::vector kwonly_args = {"gamma"}; + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; - auto [args, kwonly] = OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); + auto [args, kwonly] = + OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); - EXPECT_EQ(args.size(), 2); - EXPECT_EQ(args[0], "0.5"); - EXPECT_EQ(args[1], "1.0"); - - EXPECT_EQ(kwonly.size(), 1); - EXPECT_EQ(kwonly["gamma"], "2.0"); -} + EXPECT_EQ(args.size(), 2); + EXPECT_EQ(args[0], "0.5"); + EXPECT_EQ(args[1], "1.0"); + EXPECT_EQ(kwonly.size(), 1); + EXPECT_EQ(kwonly["gamma"], "2.0"); +} TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { - std::map kwargs = { - {"beta", "1.0"}, - {"gamma", "2.0"} - }; + std::map kwargs = {{"beta", "1.0"}, + {"gamma", "2.0"}}; - std::vector required_args = {"alpha", "beta"}; - std::vector kwonly_args = {"gamma"}; + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; - EXPECT_THROW(OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), std::invalid_argument); + EXPECT_THROW( + OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), + std::invalid_argument); } - - - - From 18a69d1bbc5f551ad092336d554e952f3050282b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 17:56:42 -0800 Subject: [PATCH 011/311] Adding evolution API header Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 runtime/cudaq/evolution.h diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h new file mode 100644 index 0000000000..3b86c448ce --- /dev/null +++ b/runtime/cudaq/evolution.h @@ -0,0 +1,46 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/operators.h" +#include "cudaq/schedule.h" +#include "cudaq/base_integrator.h" +#include "common/EvolveResult.h" + +#include +#include +#include +#include +#include + +namespace cudaq { +class Evolution { +public: + /// Computes the Taylor series expansion of the matrix exponential. + static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, int order = 20); + + /// Computes the evolution step matrix + static Eigen::MatrixXcd compute_step_matrix(const operator_sum &hamiltonian, const std::map &dimensions, const std::map> ¶meters, double dt, bool use_gpu = false); + + /// Adds noise channels based on collapse operators. + static void add_noise_channel_for_step(const std::string &step_kernel_name, cudaq::noise_model &noise_model, const std::vector &collapse_operators, const std::map &dimensions, const std::map> ¶meters, double dt); + + /// Launches an analog Hamiltonian kernel for quantum simulations. + static evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, const operator_sum &hamiltonian, const std::shared_ptr &schedule, int shots_count, bool is_async = false); + + /// Generates evolution kernels for the simulation. + static std::vector evolution_kernel(int num_qubits, const std::function> &, double)> &compute_step_matrix, const std::vector tlist, const std::vector>> &schedule_parameters); + + /// Evolves a single quantum state under a given hamiltonian. + static evolve_result evolve_single(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, state initial_state, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); + + /// Evolves a single or multiple quantum states under a given hamiltonian. + static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, const std::vector &initial_states, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); +}; +} \ No newline at end of file From c074ffdc0fd2d9e96c642b11ff30a1623d423c10 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 17:57:19 -0800 Subject: [PATCH 012/311] Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 69 +++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 3b86c448ce..c2915319e2 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -8,10 +8,10 @@ #pragma once +#include "common/EvolveResult.h" +#include "cudaq/base_integrator.h" #include "cudaq/operators.h" #include "cudaq/schedule.h" -#include "cudaq/base_integrator.h" -#include "common/EvolveResult.h" #include #include @@ -22,25 +22,60 @@ namespace cudaq { class Evolution { public: - /// Computes the Taylor series expansion of the matrix exponential. - static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, int order = 20); + /// Computes the Taylor series expansion of the matrix exponential. + static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, + int order = 20); - /// Computes the evolution step matrix - static Eigen::MatrixXcd compute_step_matrix(const operator_sum &hamiltonian, const std::map &dimensions, const std::map> ¶meters, double dt, bool use_gpu = false); + /// Computes the evolution step matrix + static Eigen::MatrixXcd compute_step_matrix( + const operator_sum &hamiltonian, const std::map &dimensions, + const std::map> ¶meters, double dt, + bool use_gpu = false); - /// Adds noise channels based on collapse operators. - static void add_noise_channel_for_step(const std::string &step_kernel_name, cudaq::noise_model &noise_model, const std::vector &collapse_operators, const std::map &dimensions, const std::map> ¶meters, double dt); + /// Adds noise channels based on collapse operators. + static void add_noise_channel_for_step( + const std::string &step_kernel_name, cudaq::noise_model &noise_model, + const std::vector &collapse_operators, + const std::map &dimensions, + const std::map> ¶meters, double dt); - /// Launches an analog Hamiltonian kernel for quantum simulations. - static evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, const operator_sum &hamiltonian, const std::shared_ptr &schedule, int shots_count, bool is_async = false); + /// Launches an analog Hamiltonian kernel for quantum simulations. + static evolve_result + launch_analog_hamiltonian_kernel(const std::string &target_name, + const operator_sum &hamiltonian, + const std::shared_ptr &schedule, + int shots_count, bool is_async = false); - /// Generates evolution kernels for the simulation. - static std::vector evolution_kernel(int num_qubits, const std::function> &, double)> &compute_step_matrix, const std::vector tlist, const std::vector>> &schedule_parameters); + /// Generates evolution kernels for the simulation. + static std::vector evolution_kernel( + int num_qubits, + const std::function> &, double)> + &compute_step_matrix, + const std::vector tlist, + const std::vector>> + &schedule_parameters); - /// Evolves a single quantum state under a given hamiltonian. - static evolve_result evolve_single(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, state initial_state, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); + /// Evolves a single quantum state under a given hamiltonian. + static evolve_result + evolve_single(const operator_sum &hamiltonian, + const std::map &dimensions, + const std::shared_ptr &schedule, state initial_state, + const std::vector &collapse_operators = {}, + const std::vector &observables = {}, + bool store_intermediate_results = false, + std::shared_ptr> integrator = nullptr, + std::optional shots_count = std::nullopt); - /// Evolves a single or multiple quantum states under a given hamiltonian. - static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, const std::vector &initial_states, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); + /// Evolves a single or multiple quantum states under a given hamiltonian. + static std::vector + evolve(const operator_sum &hamiltonian, const std::map &dimensions, + const std::shared_ptr &schedule, + const std::vector &initial_states, + const std::vector &collapse_operators = {}, + const std::vector &observables = {}, + bool store_intermediate_results = false, + std::shared_ptr> integrator = nullptr, + std::optional shots_count = std::nullopt); }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file From 3d51d6ea50307e697086a71ea6a1f1176e7ab827 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 15 Jan 2025 08:58:11 -0800 Subject: [PATCH 013/311] Replacing Eigen::MatrixXcd with matrix_2 Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index c2915319e2..36b1afa4f5 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -12,8 +12,8 @@ #include "cudaq/base_integrator.h" #include "cudaq/operators.h" #include "cudaq/schedule.h" +#include "cudaq/utils/tensor.h" -#include #include #include #include @@ -23,11 +23,10 @@ namespace cudaq { class Evolution { public: /// Computes the Taylor series expansion of the matrix exponential. - static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, - int order = 20); + static matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20); /// Computes the evolution step matrix - static Eigen::MatrixXcd compute_step_matrix( + static matrix_2 compute_step_matrix( const operator_sum &hamiltonian, const std::map &dimensions, const std::map> ¶meters, double dt, bool use_gpu = false); @@ -49,8 +48,8 @@ class Evolution { /// Generates evolution kernels for the simulation. static std::vector evolution_kernel( int num_qubits, - const std::function> &, double)> + const std::function< + matrix_2(const std::map> &, double)> &compute_step_matrix, const std::vector tlist, const std::vector>> From abc447f1ff0290cdfcfaef0baef6bec67e2db875 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 15 Jan 2025 16:25:04 -0800 Subject: [PATCH 014/311] * Adding Rydberg hamiltonian operator * Adding unitests for Rydberg hamiltonian operator * Making evaluate function in scalar_operator const Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 2 +- .../cudaq/dynamics/rydberg_hamiltonian.cpp | 55 ++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 2 +- runtime/cudaq/operators.h | 53 +++++++- unittests/CMakeLists.txt | 1 + unittests/dynamics/rydberg_hamiltonian.cpp | 124 ++++++++++++++++++ 6 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 runtime/cudaq/dynamics/rydberg_hamiltonian.cpp create mode 100644 unittests/dynamics/rydberg_hamiltonian.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index d608aba2c3..be4f65799c 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp ) add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) diff --git a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp new file mode 100644 index 0000000000..3d8b125ad3 --- /dev/null +++ b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include +#include + +namespace cudaq { +rydberg_hamiltonian::rydberg_hamiltonian( + const std::vector &atom_sites, const scalar_operator &litude, + const scalar_operator &phase, const scalar_operator &delta_global, + const std::vector &atom_filling, + const std::optional>> + &delta_local) + : atom_sites(atom_sites), amplitude(amplitude), phase(phase), + delta_global(delta_global), delta_local(delta_local) { + if (atom_filling.empty()) { + this->atom_filling = std::vector(atom_sites.size(), 1); + } else if (atom_sites.size() != atom_filling.size()) { + throw std::invalid_argument( + "Size of `atom_sites` and `atom_filling` must be equal."); + } else { + this->atom_filling = atom_filling; + } + + if (delta_local.has_value()) { + throw std::runtime_error( + "Local detuning is an experimental feature not yet supported."); + } +} + +const std::vector & +rydberg_hamiltonian::get_atom_sites() const { + return atom_sites; +} + +const std::vector &rydberg_hamiltonian::get_atom_filling() const { + return atom_filling; +} + +const scalar_operator &rydberg_hamiltonian::get_amplitude() const { + return amplitude; +} + +const scalar_operator &rydberg_hamiltonian::get_phase() const { return phase; } + +const scalar_operator &rydberg_hamiltonian::get_delta_global() const { + return delta_global; +} +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 1be54ea9ee..1d9716c9f3 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -40,7 +40,7 @@ scalar_operator::scalar_operator(double value) { } std::complex scalar_operator::evaluate( - std::map> parameters) { + std::map> parameters) const { return generator(parameters); } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d668909daa..121a088b49 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -64,8 +64,9 @@ class operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(const std::map &dimensions, - const std::map ¶ms = {}) const; + matrix_2 to_matrix( + const std::map &dimensions, + const std::map> ¶ms = {}) const; // Arithmetic operators operator_sum operator+(const operator_sum &other) const; @@ -398,7 +399,7 @@ class scalar_operator : public product_operator { /// @brief Return the scalar operator as a concrete complex value. std::complex - evaluate(std::map> parameters); + evaluate(std::map> parameters) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatability with the other inherited classes. @@ -461,4 +462,50 @@ void operator-=(scalar_operator &self, scalar_operator other); void operator*=(scalar_operator &self, scalar_operator other); void operator/=(scalar_operator &self, scalar_operator other); +/// @brief Representation of a time-dependent Hamiltonian for Rydberg system +class rydberg_hamiltonian : public operator_sum { +public: + using Coordinate = std::pair; + + /// @brief Constructor. + /// @param atom_sites List of 2D coordinates for trap sites. + /// @param amplitude Time-dependant driving amplitude, Omega(t). + /// @param phase Time-dependant driving phase, phi(t). + /// @param delta_global Time-dependant driving detuning, Delta_global(t). + /// @param atom_filling Optional. Marks occupied trap sites (1) and empty + /// sites (0). Defaults to all sites occupied. + /// @param delta_local Optional. A tuple of Delta_local(t) and site dependant + /// local detuning factors. + rydberg_hamiltonian( + const std::vector &atom_sites, + const scalar_operator &litude, const scalar_operator &phase, + const scalar_operator &delta_global, + const std::vector &atom_filling = {}, + const std::optional>> + &delta_local = std::nullopt); + + /// @brief Get atom sites. + const std::vector &get_atom_sites() const; + + /// @brief Get atom filling. + const std::vector &get_atom_filling() const; + + /// @brief Get amplitude operator. + const scalar_operator &get_amplitude() const; + + /// @brief Get phase operator. + const scalar_operator &get_phase() const; + + /// @brief Get global detuning operator. + const scalar_operator &get_delta_global() const; + +private: + std::vector atom_sites; + std::vector atom_filling; + scalar_operator amplitude; + scalar_operator phase; + scalar_operator delta_global; + std::optional>> delta_local; +}; + } // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f788ea0c48..f582c4d522 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -52,6 +52,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/test_runge_kutta_time_stepper.cpp dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp + dynamics/rydberg_hamiltonian.cpp ) # Make it so we can get function symbols diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp new file mode 100644 index 0000000000..2e64cb0666 --- /dev/null +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include + +using namespace cudaq; + +TEST(RydbergHamiltonianTest, ConstructorValidInputs) { + // Valid atom sites + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Valid atom filling + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + + EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); + EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); + EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), + std::complex(1.0, 0.0)); + EXPECT_EQ(hamiltonian.get_phase().evaluate({}), + std::complex(0.0, 0.0)); + EXPECT_EQ(hamiltonian.get_delta_global().evaluate({}), + std::complex(-0.5, 0.0)); +} + +TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Valid atom filling + std::vector atom_filling = {1, 0, 1}; + + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling); + + EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); + EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); +} + +TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Invalid atom filling size + std::vector atom_filling = {1, 0}; + + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling), + std::invalid_argument); +} + +TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Invalid delta_local + auto delta_local = + std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); + + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + {}, delta_local), + std::runtime_error); +} + +TEST(RydbergHamiltonianTest, Accessors) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + + EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); + EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), + std::complex(1.0, 0.0)); + EXPECT_EQ(hamiltonian.get_phase().evaluate({}), + std::complex(0.0, 0.0)); + EXPECT_EQ(hamiltonian.get_delta_global().evaluate({}), + std::complex(-0.5, 0.0)); +} + +TEST(RydbergHamiltonianTest, DefaultAtomFilling) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + + std::vector expected_filling(atom_sites.size(), 1); + EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); +} From 913d601f775bd74a213c6a9c11df86864832d551 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 16 Jan 2025 14:10:02 -0800 Subject: [PATCH 015/311] Adding interface for cudm_helpers Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 runtime/cudaq/cudm_helpers.h diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h new file mode 100644 index 0000000000..d706d5ed91 --- /dev/null +++ b/runtime/cudaq/cudm_helpers.h @@ -0,0 +1,43 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/utils/tensor.h" +#include +#include +#include +#include +#include + +namespace cudaq { +cudensitymatState_t initialize_state(cudensitymatHandle_t handle, + cudensitymatStatePurity_t purity, + int num_modes, + const std::vector &mode_extents); + +void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, + double scale_factor, cudaStream_t stream); + +void destroy_state(cudensitymatState_t state); + +cudensitymatOperator_t +compute_lindblad_operator(cudensitymatHandle_t handle, + const std::vector &c_ops, + const std::vector &mode_extents); + +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map ¶meters, const matrix_2 &matrix, + const std::vector &mode_extents); + +cudensitymatOperator_t construct_liovillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + double gamma); +} // namespace cudaq \ No newline at end of file From ea9e1d99f47f4ccf0f9078ee554c730336d95c47 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 16 Jan 2025 14:21:12 -0800 Subject: [PATCH 016/311] Changing matrix to operator_sum Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index d706d5ed91..6e1418f13e 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -9,6 +9,7 @@ #pragma once #include "cudaq/utils/tensor.h" +#include "cudaq/operators.h" #include #include #include @@ -33,7 +34,7 @@ compute_lindblad_operator(cudensitymatHandle_t handle, cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, - const std::map ¶meters, const matrix_2 &matrix, + const std::map ¶meters, const operator_sum &op, const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( From efabd770ea89b93edfb7449468ea4bb3bac15c06 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 09:10:44 -0800 Subject: [PATCH 017/311] cudm_helpers implementation Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 8 +- runtime/cudaq/dynamics/CMakeLists.txt | 13 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 201 ++++++++++++++++++ runtime/cudaq/dynamics/cudm_state.h | 27 +++ .../cudaq/dynamics/elementary_operators.cpp | 6 +- runtime/cudaq/dynamics/evolution.cpp | 99 +++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 24 ++- runtime/cudaq/dynamics/product_operators.cpp | 47 ++++ runtime/cudaq/dynamics/scalar_operators.cpp | 4 +- runtime/cudaq/evolution.h | 9 +- runtime/cudaq/operators.h | 15 +- unittests/CMakeLists.txt | 15 +- unittests/dynamics/test_cudm_helpers.cpp | 111 ++++++++++ 13 files changed, 548 insertions(+), 31 deletions(-) create mode 100644 runtime/cudaq/dynamics/cudm_helpers.cpp create mode 100644 runtime/cudaq/dynamics/cudm_state.h create mode 100644 runtime/cudaq/dynamics/evolution.cpp create mode 100644 unittests/dynamics/test_cudm_helpers.cpp diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 6e1418f13e..de4b4760ab 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -8,8 +8,8 @@ #pragma once -#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" #include #include #include @@ -30,12 +30,12 @@ void destroy_state(cudensitymatState_t state); cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, - const std::vector &mode_extents); + const std::vector &mode_extents); cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, - const std::map ¶meters, const operator_sum &op, - const std::vector &mode_extents); + const std::map> ¶meters, + const operator_sum &op, const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index be4f65799c..283e77e7ff 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -7,22 +7,31 @@ # ============================================================================ # set(LIBRARY_NAME cudaq-operators) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp ) +set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") +if (NOT DEFINED CUQUANTUM_INSTALL_PREFIX) + message(FATAL_ERROR "CUQUANTUM_INSTALL_PREFIX is not defined.") +endif() + add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) target_include_directories(${LIBRARY_NAME} PUBLIC $ $ + $ + /usr/local/cuda/targets/x86_64-linux/include $ PRIVATE .) +target_link_libraries(${LIBRARY_NAME} PRIVATE ${CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0) + set (OPERATOR_DEPENDENCIES "") list(APPEND OPERATOR_DEPENDENCIES fmt::fmt-header-only) add_openmp_configurations(${LIBRARY_NAME} OPERATOR_DEPENDENCIES) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp new file mode 100644 index 0000000000..c9248720f9 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_helpers.h" + +namespace cudaq { +cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents) { + try { + cudensitymatState_t state; + auto status = cudensitymatCreateState(handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to initialize quantum state."); + } + return state; + } catch (const std::exception &e) { + std::cerr << "Error in initialize_state: " << e.what() < &c_ops, const std::vector &mode_extents) { + try { + if (c_ops.empty()) { + throw std::invalid_argument("Collapse operators cannot be empty."); + } + + cudensitymatOperator_t lindblad_op; + auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &lindblad_op); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create lindblad operator."); + } + + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument("Collapse operator must be a square matrix."); + } + + std::vector> flat_matrix(dim * dim); + for (size_t i = 0; i < dim; i++) { + for (size_t j = 0; j < dim; j++) { + flat_matrix[i * dim + j] = c_op[{i, j}]; + } + } + + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(lindblad_op); + throw std::runtime_error("Failed to create operator term."); + } + + // Attach terms and cleanup + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to append operator term."); + } + + cudensitymatDestroyOperatorTerm(term); + } + + return lindblad_op; + } catch (const std::exception &e) { + std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; + throw; + } +} + +cudensitymatOperator_t convert_to_cudensitymat_operator(cudensitymatHandle_t handle, const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { + try { + cudensitymatOperator_t operator_handle; + auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &operator_handle); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create operator."); + } + + // Define dimensions for the operator + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } + + auto matrix = op.to_matrix(dimensions, parameters); + size_t dim = matrix.get_rows(); + if (matrix.get_columns() != dim) { + throw std::invalid_argument("Matrix must be a square."); + } + + std::vector> flat_matrix; + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } + + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to create operator term."); + } + + // Attach flat_matrix to the term + int32_t num_elem_operators = 1; + int32_t num_operator_modes = static_cast(mode_extents.size()); + const int64_t *operator_mode_extents = mode_extents.data(); + const int64_t *operator_mode_strides = nullptr; + int32_t state_modes_acted_on[static_cast(mode_extents.size())]; + for (int32_t i = 0; i < num_operator_modes; i++) { + state_modes_acted_on[i] = i; + } + + cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + + void *tensor_data = flat_matrix.data(); + cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + + status = cudensitymatOperatorTermAppendGeneralProduct(handle, term, + num_elem_operators, &num_operator_modes, &operator_mode_extents, + &operator_mode_strides, state_modes_acted_on, nullptr, + CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to attach flat_matrix to operator term."); + } + + status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to attach term to operator."); + } + + cudensitymatDestroyOperatorTerm(term); + return operator_handle; + } catch (const std::exception &e) { + std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() << std::endl; + throw; + } +} + +cudensitymatOperator_t construct_liovillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { + try { + cudensitymatOperator_t liouvillian; + auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create Liouvillian operator."); + } + + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add hamiltonian term."); + } + + cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); + for (const auto &c_op : collapse_operators) { + status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add collapse operator term."); + } + } + + return liouvillian; + } catch (const std::exception &e) { + std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; + throw; + } +} +} diff --git a/runtime/cudaq/dynamics/cudm_state.h b/runtime/cudaq/dynamics/cudm_state.h new file mode 100644 index 0000000000..1bdba17782 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_state.h @@ -0,0 +1,27 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include + +namespace cudaq { +class cudm_mat_state { +public: + cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents); + ~cudm_mat_state(); + + void scale(double factor, cudaStream_t stream); + cudensitymatState_t get() const; + +private: + cudensitymatState_t state; + cudensitymatHandle_t handle; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 137dde02cc..574891033f 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -280,9 +280,9 @@ elementary_operator::squeeze(int degree, std::complex amplitude) { } matrix_2 elementary_operator::to_matrix( - std::map dimensions, - std::map> parameters) { - return m_ops[id].generator(dimensions, parameters); + const std::map dimensions, + const std::map> parameters) const { + return m_ops.at(id).generator(dimensions, parameters); } /// Elementary Operator Arithmetic. diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp new file mode 100644 index 0000000000..69a8209607 --- /dev/null +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -0,0 +1,99 @@ +// /****************************************************************-*- C++ -*-**** +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * +// ******************************************************************************/ + +// #include "cudaq/evolution.h" +// #include +// #include +// #include +// #include + +// namespace cudaq { + +// // Can be removed +// matrix_2 taylor_series_expm(const matrix_2 &op_matrix, +// int order = 20) { +// matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); +// matrix_2 op_matrix_n = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + +// for (size_t i = 0; i < op_matrix.get_rows(); i++) { +// result[{i, i}] = std::complex(1.0, 0.0); +// op_matrix_n[{i, i}] = std::complex(1.0, 0.0); +// } + +// double factorial = 1.0; +// for (int n = 1; n <= order; n++) { +// op_matrix_n *= op_matrix; +// factorial *= n; +// result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; +// } + +// return result; +// } + +// matrix_2 compute_step_matrix( +// const operator_sum &hamiltonian, const std::map &dimensions, +// const std::map> ¶meters, double dt, +// bool use_gpu) { +// matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); +// op_matrix = dt * std::complex(0, -1) * op_matrix; + +// if (use_gpu) { +// // TODO: Implement GPU matrix exponential using CuPy or cuQuantum +// throw std::runtime_error("GPU-based matrix exponentiation not implemented."); +// } else { +// return taylor_series_expm(op_matrix); +// } +// } + +// void add_noise_channel_for_step( +// const std::string &step_kernel_name, cudaq::noise_model &noise_model, +// const std::vector &collapse_operators, +// const std::map &dimensions, +// const std::map> ¶meters, double dt) { +// for (const auto &collapse_op : collapse_operators) { +// matrix_2 L = collapse_op.to_matrix(dimensions, parameters); +// matrix_2 G = std::complex(-0.5, 0.0) * (L * L); + +// // Kraus operators +// matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); +// matrix_2 M1 = std::sqrt(dt) * L; + +// try { +// noise_model.add_all_qubit_channel(step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); +// } catch (const std::exception &e) { +// std::cerr << "Error adding noise channel: " << e.what() << std::endl; +// throw; +// } +// } +// } + +// // evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, +// // const rydberg_hamiltonian &hamiltonian, +// // const Schedule &schedule, +// // int shots_count, bool is_async = false) { +// // // Generate the time series +// // std::vector> amp_ts, ph_ts, dg_ts; + +// // auto current_schedule = schedule; +// // current_schedule.reset(); + +// // while(auto t = current_schedule.current_step()) { +// // std::map> parameters = {{"time", t.value()}}; + +// // amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), t.value().real()); +// // ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), t.value().real()); +// // dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), t.value().real()); + +// // ++schedule; +// // } + +// // // Atom arrangement and physical fields +// // cudaq::ahs::AtomArrangement atoms; + +// // } +// } \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index a0ba70cb2b..dd3227784d 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -347,12 +347,24 @@ operator_sum operator_sum::operator*=(const elementary_operator &other) { return *this; } -/// FIXME: -// tensor -// operator_sum::to_matrix(const std::map &dimensions, -// const std::map ¶ms) const { -// // todo -// } +matrix_2 operator_sum::to_matrix( + const std::map &dimensions, + const std::map> ¶ms) const { + std::size_t total_dimension = 1; + for (const auto &[_, dim] : dimensions) { + total_dimension *= dim; + } + + matrix_2 result(total_dimension, total_dimension); + + for (const auto &term : m_terms) { + matrix_2 term_matrix = term.to_matrix(dimensions, params); + + result += term_matrix; + } + + return result; +} // std::string operator_sum::to_string() const { // std::string result; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 0c8b411b1a..32adcaa339 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -35,6 +35,53 @@ product_operator::product_operator( // tensor::identity(1, 1), kronecker); // } +matrix_2 product_operator::to_matrix( + const std::map dimensions, + const std::map> parameters) const { + // Lambda functions to retrieve degrees and matrices + auto getDegrees = [](auto &&term) { return term.degrees; }; + auto getMatrix = [&](auto &&term) { + return term.to_matrix(dimensions, parameters); + }; + + // Initialize a result matrix with a single identity element + matrix_2 result(1, 1); + result[{0, 0}] = 1.0; + + // Iterate over all terms in the product operator + for (const auto &term : m_terms) { + // Get the degrees for the current term + auto termDegrees = std::visit(getDegrees, term); + bool inserted = false; + + matrix_2 termMatrix(1, 1); + termMatrix[{0, 0}] = 1.0; + + // Build the matrix list with identities or operator matrices + for (const auto &[degree, dim] : dimensions) { + if (std::find(termDegrees.begin(), termDegrees.end(), degree) != + termDegrees.end() && + !inserted) { + // Use the operator matrix for the active degree + termMatrix.kronecker_inplace(std::visit(getMatrix, term)); + inserted = true; + } else { + // Use identity matrix for other degrees + matrix_2 identityMatrix(dim, dim); + for (std::size_t i = 0; i < dim; i++) { + identityMatrix[{i, i}] = 1.0; + } + termMatrix.kronecker_inplace(identityMatrix); + } + } + + // Multiply the result matrix by the term matrix + result *= termMatrix; + } + + return result; +} + // /// IMPLEMENT: // tensor product_operator::to_matrix( // std::map dimensions, diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 1d9716c9f3..4d640b50b5 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -45,8 +45,8 @@ std::complex scalar_operator::evaluate( } matrix_2 scalar_operator::to_matrix( - std::map dimensions, - std::map> parameters) { + const std::map dimensions, + const std::map> parameters) const { auto returnOperator = matrix_2(1, 1); returnOperator[{0, 0}] = evaluate(parameters); return returnOperator; diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 36b1afa4f5..7b5f717806 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -39,11 +39,9 @@ class Evolution { const std::map> ¶meters, double dt); /// Launches an analog Hamiltonian kernel for quantum simulations. - static evolve_result - launch_analog_hamiltonian_kernel(const std::string &target_name, - const operator_sum &hamiltonian, - const std::shared_ptr &schedule, - int shots_count, bool is_async = false); + static evolve_result launch_analog_hamiltonian_kernel( + const std::string &target_name, const rydberg_hamiltonian &hamiltonian, + const Schedule &schedule, int shots_count, bool is_async = false); /// Generates evolution kernels for the simulation. static std::vector evolution_kernel( @@ -67,6 +65,7 @@ class Evolution { std::optional shots_count = std::nullopt); /// Evolves a single or multiple quantum states under a given hamiltonian. + /// Run only for dynamics target else throw error static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 121a088b49..8924dbb861 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -200,8 +200,9 @@ class product_operator : public operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); + matrix_2 + to_matrix(const std::map dimensions, + const std::map> parameters) const; /// @brief Creates a representation of the operator as a `cudaq::pauli_word` /// that can be passed as an argument to quantum kernels. @@ -279,8 +280,9 @@ class elementary_operator : public product_operator { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); + matrix_2 + to_matrix(const std::map dimensions, + const std::map> parameters) const; // Predefined operators. static elementary_operator identity(int degree); @@ -403,8 +405,9 @@ class scalar_operator : public product_operator { // Return the scalar operator as a 1x1 matrix. This is needed for // compatability with the other inherited classes. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); + matrix_2 + to_matrix(const std::map dimensions, + const std::map> parameters) const; // /// @brief Returns true if other is a scalar operator with the same // /// generator. diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f582c4d522..bc1d391943 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -53,11 +53,14 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp + dynamics/test_cudm_helpers.cpp ) # Make it so we can get function symbols set (CMAKE_ENABLE_EXPORTS TRUE) +include_directories(/usr/local/cuda/targets/x86_64-linux/include) + ## This Macro allows us to create a test_runtime executable for ## the sources in CUDAQ_RUNTIME_TEST_SOURCE for a specific backend simulator macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) @@ -80,7 +83,9 @@ macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) cudaq fmt::fmt-header-only cudaq-platform-default cudaq-builder - gtest_main) + gtest_main + $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 + /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) set(TEST_LABELS "") if (${NVQIR_BACKEND} STREQUAL "qpp") target_compile_definitions(${TEST_EXE_NAME} PRIVATE -DCUDAQ_SIMULATION_SCALAR_FP64) @@ -275,6 +280,7 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_ops_simple.cpp dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp + dynamics/test_cudm_helpers.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) @@ -285,7 +291,9 @@ target_link_libraries(test_operators cudaq-spin cudaq-operators cudaq - gtest_main) + gtest_main + $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 + /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) gtest_discover_tests(test_operators) add_subdirectory(plugin) @@ -406,7 +414,8 @@ target_link_libraries(${TEST_EXE_NAME} cudaq-platform-default cudaq-rest-qpu cudaq-builder - gtest_main) + gtest_main + $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0) set(TEST_LABELS "") if ("${TEST_LABELS}" STREQUAL "") gtest_discover_tests(${TEST_EXE_NAME}) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp new file mode 100644 index 0000000000..4e1a21306e --- /dev/null +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include + +// Initialize operator_sum +cudaq::operator_sum initialize_operator_sum() { + std::vector degrees = {0, 1}; + + // Elementary operators + cudaq::elementary_operator pauli_x("pauli_x", {0}); + cudaq::elementary_operator pauli_z("pauli_z", {1}); + cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); + + auto prod_op_1 = + cudaq::scalar_operator(std::complex(1.0, 0.0)) * pauli_x * pauli_z; + + auto prod_op_2 = + cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; + + cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + + return op_sum; +} + +class CuDensityMatTestFixture : public ::testing::Test { +protected: + cudensitymatHandle_t handle; + + void SetUp() override { + auto status = cudensitymatCreate(&handle); + ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); + } + + void TearDown() override { + cudensitymatDestroy(handle); + } +}; + +// Test for convert_to_cudensitymat_operator +TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { + std::vector mode_extents = {2, 2}; + + auto op_sum = initialize_operator_sum(); + + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + + ASSERT_NE(result, nullptr); + + cudensitymatDestroyOperator(result); +} + +// Test for compute_lindblad_op +TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { + std::vector mode_extents = {2, 2}; + + cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); + cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); + std::vector c_ops = {c_op1, c_op2}; + + auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + + ASSERT_NE(result, nullptr); + + cudensitymatDestroyOperator(result); +} + +// Test for initialize_state +TEST_F(CuDensityMatTestFixture, InitializeState) { + std::vector mode_extents = {2, 2}; + + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); + + ASSERT_NE(state, nullptr); + + cudaq::destroy_state(state); +} + +// Test for scale_state +TEST_F(CuDensityMatTestFixture, ScaleState) { + std::vector mode_extents = {2, 2}; + + ASSERT_NO_THROW({ + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); + ASSERT_NE(state, nullptr); + + cudaStream_t stream; + cudaStreamCreate(&stream); + + EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + + cudaStreamDestroy(stream); + cudaq::destroy_state(state); + }); +} + +// Test invalid handle +TEST_F(CuDensityMatTestFixture, InvalidHandle) { + cudensitymatHandle_t invalid_handle = nullptr; + + std::vector mode_extents = {2, 2}; + auto op_sum = initialize_operator_sum(); + + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, op_sum, mode_extents), std::runtime_error); +} From 10f69e1330fd4663af9f1e0bf03668b0b9531e30 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 09:13:36 -0800 Subject: [PATCH 018/311] Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 355 ++++++++++++----------- runtime/cudaq/dynamics/cudm_state.h | 15 +- runtime/cudaq/dynamics/evolution.cpp | 197 +++++++------ unittests/dynamics/test_cudm_helpers.cpp | 101 +++---- 4 files changed, 355 insertions(+), 313 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index c9248720f9..9689d06975 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -9,193 +9,224 @@ #include "cudaq/cudm_helpers.h" namespace cudaq { -cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents) { - try { - cudensitymatState_t state; - auto status = cudensitymatCreateState(handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to initialize quantum state."); - } - return state; - } catch (const std::exception &e) { - std::cerr << "Error in initialize_state: " << e.what() < &mode_extents) { + try { + cudensitymatState_t state; + auto status = cudensitymatCreateState( + handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to initialize quantum state."); } + return state; + } catch (const std::exception &e) { + std::cerr << "Error in initialize_state: " << e.what() << std::endl; + throw; + } } -void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { - try { - auto status = cudensitymatStateComputeScaling(handle, state, &scale_factor, stream); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to scale quantum state."); - } - } catch (const std::exception &e) { - std::cerr << "Error in scale_state: " << e.what() < &c_ops, const std::vector &mode_extents) { - try { - if (c_ops.empty()) { - throw std::invalid_argument("Collapse operators cannot be empty."); - } - - cudensitymatOperator_t lindblad_op; - auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &lindblad_op); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create lindblad operator."); - } - - for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument("Collapse operator must be a square matrix."); - } - - std::vector> flat_matrix(dim * dim); - for (size_t i = 0; i < dim; i++) { - for (size_t j = 0; j < dim; j++) { - flat_matrix[i * dim + j] = c_op[{i, j}]; - } - } - - // Create Operator term for LtL and add to lindblad_op - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(lindblad_op); - throw std::runtime_error("Failed to create operator term."); - } - - // Attach terms and cleanup - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to append operator term."); - } - - cudensitymatDestroyOperatorTerm(term); - } - - return lindblad_op; - } catch (const std::exception &e) { - std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; - throw; +cudensitymatOperator_t +compute_lindblad_operator(cudensitymatHandle_t handle, + const std::vector &c_ops, + const std::vector &mode_extents) { + try { + if (c_ops.empty()) { + throw std::invalid_argument("Collapse operators cannot be empty."); } -} -cudensitymatOperator_t convert_to_cudensitymat_operator(cudensitymatHandle_t handle, const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { - try { - cudensitymatOperator_t operator_handle; - auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &operator_handle); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create operator."); - } + cudensitymatOperator_t lindblad_op; + auto status = cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &lindblad_op); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create lindblad operator."); + } - // Define dimensions for the operator - std::map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument( + "Collapse operator must be a square matrix."); + } + + std::vector> flat_matrix(dim * dim); + for (size_t i = 0; i < dim; i++) { + for (size_t j = 0; j < dim; j++) { + flat_matrix[i * dim + j] = c_op[{i, j}]; + } + } + + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(lindblad_op); + throw std::runtime_error("Failed to create operator term."); + } + + // Attach terms and cleanup + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, + {1.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to append operator term."); + } + + cudensitymatDestroyOperatorTerm(term); + } - auto matrix = op.to_matrix(dimensions, parameters); - size_t dim = matrix.get_rows(); - if (matrix.get_columns() != dim) { - throw std::invalid_argument("Matrix must be a square."); - } + return lindblad_op; + } catch (const std::exception &e) { + std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; + throw; + } +} - std::vector> flat_matrix; - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); - } - } +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map> ¶meters, + const operator_sum &op, const std::vector &mode_extents) { + try { + cudensitymatOperator_t operator_handle; + auto status = cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &operator_handle); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create operator."); + } - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to create operator term."); - } + // Define dimensions for the operator + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } - // Attach flat_matrix to the term - int32_t num_elem_operators = 1; - int32_t num_operator_modes = static_cast(mode_extents.size()); - const int64_t *operator_mode_extents = mode_extents.data(); - const int64_t *operator_mode_strides = nullptr; - int32_t state_modes_acted_on[static_cast(mode_extents.size())]; - for (int32_t i = 0; i < num_operator_modes; i++) { - state_modes_acted_on[i] = i; - } + auto matrix = op.to_matrix(dimensions, parameters); + size_t dim = matrix.get_rows(); + if (matrix.get_columns() != dim) { + throw std::invalid_argument("Matrix must be a square."); + } - cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + std::vector> flat_matrix; + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } - void *tensor_data = flat_matrix.data(); - cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to create operator term."); + } - status = cudensitymatOperatorTermAppendGeneralProduct(handle, term, - num_elem_operators, &num_operator_modes, &operator_mode_extents, - &operator_mode_strides, state_modes_acted_on, nullptr, - CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to attach flat_matrix to operator term."); - } + // Attach flat_matrix to the term + int32_t num_elem_operators = 1; + int32_t num_operator_modes = static_cast(mode_extents.size()); + const int64_t *operator_mode_extents = mode_extents.data(); + const int64_t *operator_mode_strides = nullptr; + int32_t state_modes_acted_on[static_cast(mode_extents.size())]; + for (int32_t i = 0; i < num_operator_modes; i++) { + state_modes_acted_on[i] = i; + } - status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to attach term to operator."); - } + cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + + void *tensor_data = flat_matrix.data(); + cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + + status = cudensitymatOperatorTermAppendGeneralProduct( + handle, term, num_elem_operators, &num_operator_modes, + &operator_mode_extents, &operator_mode_strides, state_modes_acted_on, + nullptr, CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, + scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error( + "Failed to attach flat_matrix to operator term."); + } - cudensitymatDestroyOperatorTerm(term); - return operator_handle; - } catch (const std::exception &e) { - std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() << std::endl; - throw; + status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, + coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to attach term to operator."); } -} -cudensitymatOperator_t construct_liovillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { - try { - cudensitymatOperator_t liouvillian; - auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create Liouvillian operator."); - } + cudensitymatDestroyOperatorTerm(term); + return operator_handle; + } catch (const std::exception &e) { + std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() + << std::endl; + throw; + } +} - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add hamiltonian term."); - } +cudensitymatOperator_t construct_liovillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + double gamma) { + try { + cudensitymatOperator_t liouvillian; + auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create Liouvillian operator."); + } - cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); - for (const auto &c_op : collapse_operators) { - status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add collapse operator term."); - } - } + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, + {1.0, 0.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add hamiltonian term."); + } - return liouvillian; - } catch (const std::exception &e) { - std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; - throw; + cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); + for (const auto &c_op : collapse_operators) { + status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, + coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add collapse operator term."); + } } + + return liouvillian; + } catch (const std::exception &e) { + std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; + throw; + } } -} +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_state.h b/runtime/cudaq/dynamics/cudm_state.h index 1bdba17782..1f20f9483f 100644 --- a/runtime/cudaq/dynamics/cudm_state.h +++ b/runtime/cudaq/dynamics/cudm_state.h @@ -14,14 +14,15 @@ namespace cudaq { class cudm_mat_state { public: - cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents); - ~cudm_mat_state(); + cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, + int num_modes, const std::vector &mode_extents); + ~cudm_mat_state(); - void scale(double factor, cudaStream_t stream); - cudensitymatState_t get() const; + void scale(double factor, cudaStream_t stream); + cudensitymatState_t get() const; private: - cudensitymatState_t state; - cudensitymatHandle_t handle; + cudensitymatState_t state; + cudensitymatHandle_t handle; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 69a8209607..75e5ca0875 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -1,99 +1,106 @@ -// /****************************************************************-*- C++ -*-**** -// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * -// * All rights reserved. * -// * * -// * This source code and the accompanying materials are made available under * -// * the terms of the Apache License 2.0 which accompanies this distribution. * -// ******************************************************************************/ - -// #include "cudaq/evolution.h" -// #include -// #include -// #include -// #include - -// namespace cudaq { - -// // Can be removed -// matrix_2 taylor_series_expm(const matrix_2 &op_matrix, -// int order = 20) { -// matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); -// matrix_2 op_matrix_n = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); - -// for (size_t i = 0; i < op_matrix.get_rows(); i++) { -// result[{i, i}] = std::complex(1.0, 0.0); -// op_matrix_n[{i, i}] = std::complex(1.0, 0.0); +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/evolution.h" +#include +#include +#include +#include + +namespace cudaq { + +// Can be removed +matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20) { + matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + matrix_2 op_matrix_n = + matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + + for (size_t i = 0; i < op_matrix.get_rows(); i++) { + result[{i, i}] = std::complex(1.0, 0.0); + op_matrix_n[{i, i}] = std::complex(1.0, 0.0); + } + + double factorial = 1.0; + for (int n = 1; n <= order; n++) { + op_matrix_n *= op_matrix; + factorial *= n; + result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; + } + + return result; +} + +matrix_2 compute_step_matrix( + const operator_sum &hamiltonian, const std::map &dimensions, + const std::map> ¶meters, double dt, + bool use_gpu) { + matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); + op_matrix = dt * std::complex(0, -1) * op_matrix; + + if (use_gpu) { + // TODO: Implement GPU matrix exponential using CuPy or cuQuantum + throw std::runtime_error( + "GPU-based matrix exponentiation not implemented."); + } else { + return taylor_series_expm(op_matrix); + } +} + +void add_noise_channel_for_step( + const std::string &step_kernel_name, cudaq::noise_model &noise_model, + const std::vector &collapse_operators, + const std::map &dimensions, + const std::map> ¶meters, double dt) { + for (const auto &collapse_op : collapse_operators) { + matrix_2 L = collapse_op.to_matrix(dimensions, parameters); + matrix_2 G = std::complex(-0.5, 0.0) * (L * L); + + // Kraus operators + matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); + matrix_2 M1 = std::sqrt(dt) * L; + + try { + noise_model.add_all_qubit_channel( + step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); + } catch (const std::exception &e) { + std::cerr << "Error adding noise channel: " << e.what() << std::endl; + throw; + } + } +} + +// evolve_result launch_analog_hamiltonian_kernel(const std::string +// &target_name, +// const rydberg_hamiltonian &hamiltonian, +// const Schedule &schedule, +// int shots_count, bool is_async = false) { +// // Generate the time series +// std::vector> amp_ts, ph_ts, dg_ts; + +// auto current_schedule = schedule; +// current_schedule.reset(); + +// while(auto t = current_schedule.current_step()) { +// std::map> parameters = {{"time", +// t.value()}}; + +// amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), +// t.value().real()); +// ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), +// t.value().real()); +// dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), +// t.value().real()); + +// ++schedule; // } -// double factorial = 1.0; -// for (int n = 1; n <= order; n++) { -// op_matrix_n *= op_matrix; -// factorial *= n; -// result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; -// } - -// return result; -// } +// // Atom arrangement and physical fields +// cudaq::ahs::AtomArrangement atoms; -// matrix_2 compute_step_matrix( -// const operator_sum &hamiltonian, const std::map &dimensions, -// const std::map> ¶meters, double dt, -// bool use_gpu) { -// matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); -// op_matrix = dt * std::complex(0, -1) * op_matrix; - -// if (use_gpu) { -// // TODO: Implement GPU matrix exponential using CuPy or cuQuantum -// throw std::runtime_error("GPU-based matrix exponentiation not implemented."); -// } else { -// return taylor_series_expm(op_matrix); -// } -// } - -// void add_noise_channel_for_step( -// const std::string &step_kernel_name, cudaq::noise_model &noise_model, -// const std::vector &collapse_operators, -// const std::map &dimensions, -// const std::map> ¶meters, double dt) { -// for (const auto &collapse_op : collapse_operators) { -// matrix_2 L = collapse_op.to_matrix(dimensions, parameters); -// matrix_2 G = std::complex(-0.5, 0.0) * (L * L); - -// // Kraus operators -// matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); -// matrix_2 M1 = std::sqrt(dt) * L; - -// try { -// noise_model.add_all_qubit_channel(step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); -// } catch (const std::exception &e) { -// std::cerr << "Error adding noise channel: " << e.what() << std::endl; -// throw; -// } -// } // } - -// // evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, -// // const rydberg_hamiltonian &hamiltonian, -// // const Schedule &schedule, -// // int shots_count, bool is_async = false) { -// // // Generate the time series -// // std::vector> amp_ts, ph_ts, dg_ts; - -// // auto current_schedule = schedule; -// // current_schedule.reset(); - -// // while(auto t = current_schedule.current_step()) { -// // std::map> parameters = {{"time", t.value()}}; - -// // amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), t.value().real()); -// // ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), t.value().real()); -// // dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), t.value().real()); - -// // ++schedule; -// // } - -// // // Atom arrangement and physical fields -// // cudaq::ahs::AtomArrangement atoms; - -// // } -// } \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 4e1a21306e..4ec8f100ba 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -11,101 +11,104 @@ // Initialize operator_sum cudaq::operator_sum initialize_operator_sum() { - std::vector degrees = {0, 1}; + std::vector degrees = {0, 1}; - // Elementary operators - cudaq::elementary_operator pauli_x("pauli_x", {0}); - cudaq::elementary_operator pauli_z("pauli_z", {1}); - cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); + // Elementary operators + cudaq::elementary_operator pauli_x("pauli_x", {0}); + cudaq::elementary_operator pauli_z("pauli_z", {1}); + cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); - auto prod_op_1 = - cudaq::scalar_operator(std::complex(1.0, 0.0)) * pauli_x * pauli_z; + auto prod_op_1 = cudaq::scalar_operator(std::complex(1.0, 0.0)) * + pauli_x * pauli_z; - auto prod_op_2 = - cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; + auto prod_op_2 = + cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; - cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); - return op_sum; + return op_sum; } class CuDensityMatTestFixture : public ::testing::Test { protected: - cudensitymatHandle_t handle; + cudensitymatHandle_t handle; - void SetUp() override { - auto status = cudensitymatCreate(&handle); - ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); - } + void SetUp() override { + auto status = cudensitymatCreate(&handle); + ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); + } - void TearDown() override { - cudensitymatDestroy(handle); - } + void TearDown() override { cudensitymatDestroy(handle); } }; // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); - auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + auto result = + cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); + ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); + cudensitymatDestroyOperator(result); } // Test for compute_lindblad_op TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); - cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); - std::vector c_ops = {c_op1, c_op2}; + cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); + cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); + std::vector c_ops = {c_op1, c_op2}; - auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - ASSERT_NE(result, nullptr); + ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); + cudensitymatDestroyOperator(result); } // Test for initialize_state TEST_F(CuDensityMatTestFixture, InitializeState) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + 2, mode_extents); - ASSERT_NE(state, nullptr); + ASSERT_NE(state, nullptr); - cudaq::destroy_state(state); + cudaq::destroy_state(state); } // Test for scale_state TEST_F(CuDensityMatTestFixture, ScaleState) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - ASSERT_NO_THROW({ - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); - ASSERT_NE(state, nullptr); + ASSERT_NO_THROW({ + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + 2, mode_extents); + ASSERT_NE(state, nullptr); - cudaStream_t stream; - cudaStreamCreate(&stream); + cudaStream_t stream; + cudaStreamCreate(&stream); - EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); - cudaStreamDestroy(stream); - cudaq::destroy_state(state); - }); + cudaStreamDestroy(stream); + cudaq::destroy_state(state); + }); } // Test invalid handle TEST_F(CuDensityMatTestFixture, InvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; + cudensitymatHandle_t invalid_handle = nullptr; - std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + std::vector mode_extents = {2, 2}; + auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, op_sum, mode_extents), std::runtime_error); + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, + op_sum, mode_extents), + std::runtime_error); } From 0259b909670f314197e2c2f77455c82b524fedd8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 14:37:29 -0800 Subject: [PATCH 019/311] * Adding macro for handling error * Using CUDA_C_64F data type for complex Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_error_handling.h | 29 +++++++++++++++++++++++++ runtime/cudaq/cudm_helpers.h | 1 - runtime/cudaq/dynamics/cudm_helpers.cpp | 9 +++----- 3 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 runtime/cudaq/cudm_error_handling.h diff --git a/runtime/cudaq/cudm_error_handling.h b/runtime/cudaq/cudm_error_handling.h new file mode 100644 index 0000000000..d1b8ac734f --- /dev/null +++ b/runtime/cudaq/cudm_error_handling.h @@ -0,0 +1,29 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include + +#define HANDLE_CUDM_ERROR(x) \ +{ \ + const auto err = x; \ + if (err != CUDENSITYMAT_STATUS_SUCCESS) { \ + throw std::runtime_error(fmt::format("[cudaq] %{} in {} (line {})", err \ + __FUNCTION__, __LINE__)); \ + } \ +} + +#define HANDLE_CUDA_ERROR(x) \ +{ \ + const auto err = x; \ + if (err != cudaSuccess) { \ + throw std::runtime_error(fmt::format("[cuda] %{} in {} (line {})", err \ + __FUNCTION__, __LINE__)); \ + } \ +} \ No newline at end of file diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index de4b4760ab..c2968229fb 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -19,7 +19,6 @@ namespace cudaq { cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, - int num_modes, const std::vector &mode_extents); void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 9689d06975..785057fbc6 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -11,15 +11,12 @@ namespace cudaq { cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, - int num_modes, const std::vector &mode_extents) { try { cudensitymatState_t state; - auto status = cudensitymatCreateState( - handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to initialize quantum state."); - } + HANDLE_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), + mode_extents.data(), 1, CUDA_C_64F, + &state)); return state; } catch (const std::exception &e) { std::cerr << "Error in initialize_state: " << e.what() << std::endl; From 0415b0df22cd6dc30af50d61ba53209b18abdab7 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 18:09:29 -0800 Subject: [PATCH 020/311] Adding MACRO for error handling Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_error_handling.h | 33 +++---- runtime/cudaq/dynamics/cudm_helpers.cpp | 110 ++++++++--------------- unittests/dynamics/test_cudm_helpers.cpp | 4 +- 3 files changed, 58 insertions(+), 89 deletions(-) diff --git a/runtime/cudaq/cudm_error_handling.h b/runtime/cudaq/cudm_error_handling.h index d1b8ac734f..d72be33906 100644 --- a/runtime/cudaq/cudm_error_handling.h +++ b/runtime/cudaq/cudm_error_handling.h @@ -8,22 +8,23 @@ #pragma once #include +#include #include -#define HANDLE_CUDM_ERROR(x) \ -{ \ - const auto err = x; \ - if (err != CUDENSITYMAT_STATUS_SUCCESS) { \ - throw std::runtime_error(fmt::format("[cudaq] %{} in {} (line {})", err \ - __FUNCTION__, __LINE__)); \ - } \ -} +#define HANDLE_CUDM_ERROR(x) \ + { \ + const auto err = x; \ + if (err != CUDENSITYMAT_STATUS_SUCCESS) { \ + throw std::runtime_error(fmt::format("[cudaq] %{} in {} (line {})", err, \ + __FUNCTION__, __LINE__)); \ + } \ + } -#define HANDLE_CUDA_ERROR(x) \ -{ \ - const auto err = x; \ - if (err != cudaSuccess) { \ - throw std::runtime_error(fmt::format("[cuda] %{} in {} (line {})", err \ - __FUNCTION__, __LINE__)); \ - } \ -} \ No newline at end of file +#define HANDLE_CUDA_ERROR(x) \ + { \ + const auto err = x; \ + if (err != cudaSuccess) { \ + throw std::runtime_error(fmt::format("[cuda] %{} in {} (line {})", err, \ + __FUNCTION__, __LINE__)); \ + } \ + } diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 785057fbc6..914a02914b 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -7,102 +7,70 @@ ******************************************************************************/ #include "cudaq/cudm_helpers.h" +#include "cudaq/cudm_error_handling.h" namespace cudaq { cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, const std::vector &mode_extents) { - try { - cudensitymatState_t state; - HANDLE_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), - mode_extents.data(), 1, CUDA_C_64F, - &state)); - return state; - } catch (const std::exception &e) { - std::cerr << "Error in initialize_state: " << e.what() << std::endl; - throw; - } + cudensitymatState_t state; + HANDLE_CUDM_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), + mode_extents.data(), 1, CUDA_C_64F, + &state)); + return state; } void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { - try { - auto status = - cudensitymatStateComputeScaling(handle, state, &scale_factor, stream); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to scale quantum state."); - } - } catch (const std::exception &e) { - std::cerr << "Error in scale_state: " << e.what() << std::endl; - throw; - } + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); } void destroy_state(cudensitymatState_t state) { - try { - cudensitymatDestroyState(state); - } catch (const std::exception &e) { - std::cerr << "Error in destroy_state: " << e.what() << std::endl; - } + cudensitymatDestroyState(state); } cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents) { - try { - if (c_ops.empty()) { - throw std::invalid_argument("Collapse operators cannot be empty."); - } - - cudensitymatOperator_t lindblad_op; - auto status = cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &lindblad_op); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create lindblad operator."); - } - - for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument( - "Collapse operator must be a square matrix."); - } + if (c_ops.empty()) { + throw std::invalid_argument("Collapse operators cannot be empty."); + } - std::vector> flat_matrix(dim * dim); - for (size_t i = 0; i < dim; i++) { - for (size_t j = 0; j < dim; j++) { - flat_matrix[i * dim + j] = c_op[{i, j}]; - } - } + cudensitymatOperator_t lindblad_op; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &lindblad_op)); - // Create Operator term for LtL and add to lindblad_op - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(lindblad_op); - throw std::runtime_error("Failed to create operator term."); - } + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument("Collapse operator must be a square matrix."); + } - // Attach terms and cleanup - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, - {1.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to append operator term."); + std::vector> flat_matrix(dim * dim); + for (size_t i = 0; i < dim; i++) { + for (size_t j = 0; j < dim; j++) { + flat_matrix[i * dim + j] = c_op[{i, j}]; } - - cudensitymatDestroyOperatorTerm(term); } - return lindblad_op; - } catch (const std::exception &e) { - std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; - throw; + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term)); + cudensitymatDestroyOperator(lindblad_op); + + // Attach terms and cleanup + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, + 0, {1.0}, scalarCallback)); + cudensitymatDestroyOperatorTerm(term); } + + return lindblad_op; } cudensitymatOperator_t convert_to_cudensitymat_operator( diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 4ec8f100ba..ddceb989b9 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -75,7 +75,7 @@ TEST_F(CuDensityMatTestFixture, InitializeState) { std::vector mode_extents = {2, 2}; auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - 2, mode_extents); + mode_extents); ASSERT_NE(state, nullptr); @@ -88,7 +88,7 @@ TEST_F(CuDensityMatTestFixture, ScaleState) { ASSERT_NO_THROW({ auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - 2, mode_extents); + mode_extents); ASSERT_NE(state, nullptr); cudaStream_t stream; From 09252caf5d1b6c30d6f51a4c5ea85d17f70e6efe Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 22 Jan 2025 08:59:06 -0800 Subject: [PATCH 021/311] Fixing convert_to_cudensitymat_operator as per the cudensitymat APIs Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 125 ++++++++++++------------ runtime/cudaq/operators.h | 5 +- 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 914a02914b..1fd7567f51 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -73,6 +73,15 @@ compute_lindblad_operator(cudensitymatHandle_t handle, return lindblad_op; } +std::map +convert_dimensions(const std::vector &mode_extents) { + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } + return dimensions; +} + cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, @@ -86,71 +95,67 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( throw std::runtime_error("Failed to create operator."); } - // Define dimensions for the operator - std::map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } - - auto matrix = op.to_matrix(dimensions, parameters); - size_t dim = matrix.get_rows(); - if (matrix.get_columns() != dim) { - throw std::invalid_argument("Matrix must be a square."); - } - - std::vector> flat_matrix; - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); + for (const auto &product_op : op.get_terms()) { + cudensitymatOperatorTerm_t term; + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term)); + + for (const auto &component : product_op.get_terms()) { + if (std::holds_alternative(component)) { + const auto &elem_op = std::get(component); + + // Create a cudensitymat elementary operator + cudensitymatElementaryOperator_t cudm_elem_op; + + // Get the matrix representation of elementary operator + auto dimensions = convert_dimensions(mode_extents); + auto matrix = elem_op.to_matrix(dimensions, parameters); + + // Flatten the matrix into a single-dimensional array + std::vector> flat_matrix; + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } + + // Create a cudensitymat elementary operator + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, 1, mode_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, + flat_matrix.data(), {nullptr, nullptr}, &cudm_elem_op)); + + // Append the elementary operator to the term + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 1, &cudm_elem_op, &elem_op.degrees[0], nullptr, + make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); + + // Destroy the elementary operator after appending + HANDLE_CUDM_ERROR( + cudensitymatDestroyElementaryOperator(cudm_elem_op)); + } else if (std::holds_alternative(component)) { + const auto &scalar_op = std::get(component); + + // Use the scalar coefficient + auto coeff = scalar_op.evaluate(parameters); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 0, nullptr, nullptr, nullptr, + {make_cuDoubleComplex(coeff.real(), coeff.imag())}, + {nullptr, nullptr})); + } } - } - - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to create operator term."); - } - - // Attach flat_matrix to the term - int32_t num_elem_operators = 1; - int32_t num_operator_modes = static_cast(mode_extents.size()); - const int64_t *operator_mode_extents = mode_extents.data(); - const int64_t *operator_mode_strides = nullptr; - int32_t state_modes_acted_on[static_cast(mode_extents.size())]; - for (int32_t i = 0; i < num_operator_modes; i++) { - state_modes_acted_on[i] = i; - } - - cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - void *tensor_data = flat_matrix.data(); - cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + // Append the product operator term to the top-level operator + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); - status = cudensitymatOperatorTermAppendGeneralProduct( - handle, term, num_elem_operators, &num_operator_modes, - &operator_mode_extents, &operator_mode_strides, state_modes_acted_on, - nullptr, CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, - scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error( - "Failed to attach flat_matrix to operator term."); + // Destroy the term + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); } - status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, - coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to attach term to operator."); - } - - cudensitymatDestroyOperatorTerm(term); return operator_handle; } catch (const std::exception &e) { std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 8924dbb861..fe088dbd79 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -125,7 +125,7 @@ class operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector get_terms() { return m_terms; } + std::vector get_terms() const { return m_terms; } }; operator_sum operator*(std::complex other, operator_sum self); operator_sum operator+(std::complex other, operator_sum self); @@ -218,7 +218,8 @@ class product_operator : public operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() { + std::vector> + get_terms() const { return m_terms; }; }; From b06aef21b4240b3d240648bd30650312c2b0896f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 22 Jan 2025 11:36:52 -0800 Subject: [PATCH 022/311] Updating CMakeLists and unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 13 +++- unittests/CMakeLists.txt | 4 +- unittests/dynamics/test_cudm_helpers.cpp | 75 ++++++++++++------------ 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 1fd7567f51..dbd830e92c 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -14,14 +14,21 @@ cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, const std::vector &mode_extents) { cudensitymatState_t state; - HANDLE_CUDM_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), - mode_extents.data(), 1, CUDA_C_64F, - &state)); + cudensitymatStatus_t status = + cudensitymatCreateState(handle, purity, mode_extents.size(), + mode_extents.data(), 1, CUDA_C_64F, &state); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + std::cerr << "Error in cudensitymatCreateState: " << status << std::endl; + } return state; } void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { + if (!state) { + throw std::invalid_argument("Invalid state provided to scale_state."); + } + HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); } diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index bc1d391943..f87a25d1ec 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -293,7 +293,8 @@ target_link_libraries(test_operators cudaq gtest_main $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 - /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) + /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12 + fmt::fmt-header-only) gtest_discover_tests(test_operators) add_subdirectory(plugin) @@ -457,4 +458,3 @@ if (CUDAQ_ENABLE_PYTHON) gtest_discover_tests(test_domains TEST_SUFFIX _Sampling PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/python") endif() - diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index ddceb989b9..9b86448bc6 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -6,6 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include #include #include @@ -32,27 +33,41 @@ cudaq::operator_sum initialize_operator_sum() { class CuDensityMatTestFixture : public ::testing::Test { protected: cudensitymatHandle_t handle; + cudaStream_t stream; void SetUp() override { - auto status = cudensitymatCreate(&handle); - ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + HANDLE_CUDA_ERROR(cudaStreamCreate(&stream)); } - void TearDown() override { cudensitymatDestroy(handle); } + void TearDown() override { + HANDLE_CUDA_ERROR(cudaStreamDestroy(stream)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle)); + } }; -// Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { +// Test for initialize_state +TEST_F(CuDensityMatTestFixture, InitializeState) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + mode_extents); + ASSERT_NE(state, nullptr); - auto result = - cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + cudaq::destroy_state(state); +} - ASSERT_NE(result, nullptr); +// Test for scale_state +TEST_F(CuDensityMatTestFixture, ScaleState) { + std::vector mode_extents = {2}; - cudensitymatDestroyOperator(result); + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + mode_extents); + ASSERT_NE(state, nullptr); + + EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + + cudaq::destroy_state(state); } // Test for compute_lindblad_op @@ -63,42 +78,24 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); std::vector c_ops = {c_op1, c_op2}; - auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - - ASSERT_NE(result, nullptr); - - cudensitymatDestroyOperator(result); -} - -// Test for initialize_state -TEST_F(CuDensityMatTestFixture, InitializeState) { - std::vector mode_extents = {2, 2}; - - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - - ASSERT_NE(state, nullptr); + auto lindblad_op = + cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + ASSERT_NE(lindblad_op, nullptr); - cudaq::destroy_state(state); + cudensitymatDestroyOperator(lindblad_op); } -// Test for scale_state -TEST_F(CuDensityMatTestFixture, ScaleState) { +// Test for convert_to_cudensitymat_operator +TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - ASSERT_NO_THROW({ - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - ASSERT_NE(state, nullptr); - - cudaStream_t stream; - cudaStreamCreate(&stream); + auto op_sum = initialize_operator_sum(); - EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + auto result = + cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); - cudaStreamDestroy(stream); - cudaq::destroy_state(state); - }); + cudensitymatDestroyOperator(result); } // Test invalid handle From ba35c89d8df2adbac7a0162e52fdcb8de6a908b4 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 23 Jan 2025 09:06:53 -0800 Subject: [PATCH 023/311] * Adding vector of cudensitymatElementaryOperator_t to store and destroy the cudensitymatElementaryOperator_t * Fetching subspace_extents using elementary_op degrees * Adding a vector with correct action duality Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 38 +++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index dbd830e92c..ea6972f715 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -102,6 +102,8 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( throw std::runtime_error("Failed to create operator."); } + std::vector elementary_operators; + for (const auto &product_op : op.get_terms()) { cudensitymatOperatorTerm_t term; @@ -113,6 +115,14 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( if (std::holds_alternative(component)) { const auto &elem_op = std::get(component); + std::vector subspace_extents; + for (int degree : elem_op.degrees) { + if (degree > mode_extents.size()) { + throw std::out_of_range("Degree exceeds mode_extents size."); + } + subspace_extents.push_back(mode_extents[degree]); + } + // Create a cudensitymat elementary operator cudensitymatElementaryOperator_t cudm_elem_op; @@ -130,23 +140,28 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( // Create a cudensitymat elementary operator HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, 1, mode_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, - flat_matrix.data(), {nullptr, nullptr}, &cudm_elem_op)); + handle, static_cast(subspace_extents.size()), + subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, + nullptr, CUDA_C_64F, flat_matrix.data(), {nullptr, nullptr}, + &cudm_elem_op)); + + elementary_operators.push_back(cudm_elem_op); + + // Default to left action + std::vector modeActionDuality(elem_op.degrees.size(), 0); // Append the elementary operator to the term HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 1, &cudm_elem_op, &elem_op.degrees[0], nullptr, - make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); - - // Destroy the elementary operator after appending - HANDLE_CUDM_ERROR( - cudensitymatDestroyElementaryOperator(cudm_elem_op)); + handle, term, 1, &cudm_elem_op, elem_op.degrees.data(), + modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); } else if (std::holds_alternative(component)) { const auto &scalar_op = std::get(component); // Use the scalar coefficient auto coeff = scalar_op.evaluate(parameters); + // TODO: Implement handling for time-dependent scalars using + // cudensitymatScalarCallback_t HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle, term, 0, nullptr, nullptr, nullptr, {make_cuDoubleComplex(coeff.real(), coeff.imag())}, @@ -161,6 +176,11 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( // Destroy the term HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + + // Cleanup + for (auto &elem_op : elementary_operators) { + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + } } return operator_handle; From 6ce13c0737b0b9deb103b026c5b6c61109fdd481 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 23 Jan 2025 09:42:11 -0800 Subject: [PATCH 024/311] Refactoring the code Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 157 ++++++++++++++---------- 1 file changed, 91 insertions(+), 66 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index ea6972f715..0993fa7f7d 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -10,6 +10,82 @@ #include "cudaq/cudm_error_handling.h" namespace cudaq { +// Function to flatten a matrix into a 1D array +std::vector> flatten_matrix(const matrix_2 &matrix) { + std::vector> flat_matrix; + + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } + + return flat_matrix; +} + +// Function to extract sub-space extents based on degrees +std::vector +get_subspace_extents(const std::vector &mode_extents, + const std::vector °rees) { + std::vector subspace_extents; + + for (int degree : degrees) { + if (degree >= mode_extents.size()) { + throw std::out_of_range("Degree exceeds mode_extents size."); + } + subspace_extents.push_back(mode_extents[degree]); + } + + return subspace_extents; +} + +// Function to create a cudensitymat elementary operator +cudensitymatElementaryOperator_t create_elementary_operator( + cudensitymatHandle_t handle, const std::vector &subspace_extents, + const std::vector> &flat_matrix) { + cudensitymatElementaryOperator_t cudm_elem_op; + + std::vector interleaved_matrix; + interleaved_matrix.reserve(flat_matrix.size() * 2); + + for (const auto &value : flat_matrix) { + interleaved_matrix.push_back(value.real()); + interleaved_matrix.push_back(value.imag()); + } + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, static_cast(subspace_extents.size()), + subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, + CUDA_C_64F, static_cast(interleaved_matrix.data()), + {nullptr, nullptr}, &cudm_elem_op)); + + return cudm_elem_op; +} + +// Function to append an elementary operator to a term +void append_elementary_operator_to_term( + cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, + cudensitymatElementaryOperator_t &elem_op, + const std::vector °rees) { + std::vector modeActionDuality(degrees.size(), 0); + + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, static_cast(degrees.size()), &elem_op, + degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); +} + +// Function to create and append a scalar to a term +void append_scalar_to_term(cudensitymatHandle_t handle, + cudensitymatOperatorTerm_t term, + const std::complex &coeff) { + // TODO: Implement handling for time-dependent scalars using + // cudensitymatScalarCallback_t + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 0, nullptr, nullptr, nullptr, + {make_cuDoubleComplex(coeff.real(), coeff.imag())}, {nullptr, nullptr})); +} + cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, const std::vector &mode_extents) { @@ -51,26 +127,15 @@ compute_lindblad_operator(cudensitymatHandle_t handle, &lindblad_op)); for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument("Collapse operator must be a square matrix."); - } + auto flat_matrix = flatten_matrix(c_op); - std::vector> flat_matrix(dim * dim); - for (size_t i = 0; i < dim; i++) { - for (size_t j = 0; j < dim; j++) { - flat_matrix[i * dim + j] = c_op[{i, j}]; - } - } - - // Create Operator term for LtL and add to lindblad_op cudensitymatOperatorTerm_t term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); cudensitymatDestroyOperator(lindblad_op); - // Attach terms and cleanup + // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback)); @@ -95,12 +160,9 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( const operator_sum &op, const std::vector &mode_extents) { try { cudensitymatOperator_t operator_handle; - auto status = cudensitymatCreateOperator( + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle, static_cast(mode_extents.size()), mode_extents.data(), - &operator_handle); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create operator."); - } + &operator_handle)); std::vector elementary_operators; @@ -115,57 +177,20 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( if (std::holds_alternative(component)) { const auto &elem_op = std::get(component); - std::vector subspace_extents; - for (int degree : elem_op.degrees) { - if (degree > mode_extents.size()) { - throw std::out_of_range("Degree exceeds mode_extents size."); - } - subspace_extents.push_back(mode_extents[degree]); - } - - // Create a cudensitymat elementary operator - cudensitymatElementaryOperator_t cudm_elem_op; - - // Get the matrix representation of elementary operator - auto dimensions = convert_dimensions(mode_extents); - auto matrix = elem_op.to_matrix(dimensions, parameters); - - // Flatten the matrix into a single-dimensional array - std::vector> flat_matrix; - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); - } - } - - // Create a cudensitymat elementary operator - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, static_cast(subspace_extents.size()), - subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, - nullptr, CUDA_C_64F, flat_matrix.data(), {nullptr, nullptr}, - &cudm_elem_op)); + auto subspace_extents = + get_subspace_extents(mode_extents, elem_op.degrees); + auto flat_matrix = flatten_matrix( + elem_op.to_matrix(convert_dimensions(mode_extents), parameters)); + auto cudm_elem_op = + create_elementary_operator(handle, subspace_extents, flat_matrix); elementary_operators.push_back(cudm_elem_op); - - // Default to left action - std::vector modeActionDuality(elem_op.degrees.size(), 0); - - // Append the elementary operator to the term - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 1, &cudm_elem_op, elem_op.degrees.data(), - modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), - {nullptr, nullptr})); + append_elementary_operator_to_term(handle, term, cudm_elem_op, + elem_op.degrees); } else if (std::holds_alternative(component)) { - const auto &scalar_op = std::get(component); - - // Use the scalar coefficient - auto coeff = scalar_op.evaluate(parameters); - // TODO: Implement handling for time-dependent scalars using - // cudensitymatScalarCallback_t - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 0, nullptr, nullptr, nullptr, - {make_cuDoubleComplex(coeff.real(), coeff.imag())}, - {nullptr, nullptr})); + auto coeff = + std::get(component).evaluate(parameters); + append_scalar_to_term(handle, term, coeff); } } From e150ee074457bf46bb1223297950fe02b3ccf627 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 23 Jan 2025 15:41:54 -0800 Subject: [PATCH 025/311] Fixing the matrix in the unittest and other code in compute_lindblad apart for temp vector Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 21 +++++++++++++++++++-- runtime/cudaq/utils/tensor.cpp | 1 + unittests/dynamics/test_cudm_helpers.cpp | 4 ++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 0993fa7f7d..999ba90ff5 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -127,19 +127,36 @@ compute_lindblad_operator(cudensitymatHandle_t handle, &lindblad_op)); for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument("Collapse operator must be a square matrix"); + } + auto flat_matrix = flatten_matrix(c_op); + // Create Operator term for LtL and add to lindblad_op cudensitymatOperatorTerm_t term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); - cudensitymatDestroyOperator(lindblad_op); + + // Create elementary operator form c_op + auto cudm_elem_op = + create_elementary_operator(handle, mode_extents, flat_matrix); + + // Add the elementary operator to the term + // TODO: Fix temp vector below + std::vector temp; + append_elementary_operator_to_term(handle, term, cudm_elem_op, temp); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback)); - cudensitymatDestroyOperatorTerm(term); + + // Destroy intermediate resources + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(cudm_elem_op)); } return lindblad_op; diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index f37f959090..6d4aaa9764 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/utils/tensor.h" +#include #include inline std::complex &access(std::complex *p, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 9b86448bc6..734470365a 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -74,8 +74,8 @@ TEST_F(CuDensityMatTestFixture, ScaleState) { TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { std::vector mode_extents = {2, 2}; - cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); - cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); + cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); + cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); std::vector c_ops = {c_op1, c_op2}; auto lindblad_op = From 2b269749ff31563be8f136bd4e9258247976494f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 24 Jan 2025 21:39:13 -0800 Subject: [PATCH 026/311] Adding cudm_state with unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 76 +++++++++++ runtime/cudaq/dynamics/CMakeLists.txt | 2 +- runtime/cudaq/dynamics/cudm_state.cpp | 173 +++++++++++++++++++++++++ runtime/cudaq/dynamics/cudm_state.h | 28 ---- unittests/CMakeLists.txt | 2 + unittests/dynamics/test_cudm_state.cpp | 117 +++++++++++++++++ 6 files changed, 369 insertions(+), 29 deletions(-) create mode 100644 runtime/cudaq/cudm_state.h create mode 100644 runtime/cudaq/dynamics/cudm_state.cpp delete mode 100644 runtime/cudaq/dynamics/cudm_state.h create mode 100644 unittests/dynamics/test_cudm_state.cpp diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h new file mode 100644 index 0000000000..814a7ad342 --- /dev/null +++ b/runtime/cudaq/cudm_state.h @@ -0,0 +1,76 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace cudaq { +class cudm_mat_state { +public: + /// @brief To initialize state with raw data. + explicit cudm_mat_state(std::vector> rawData); + + /// @brief Destructor to clean up resources + ~cudm_mat_state(); + + /// @brief Initialize the state as a density matrix or state vector based on + /// dimensions. + /// @param hilbertSpaceDims Vector representing the Hilbert Space dimensions. + void init_state(const std::vector &hilbertSpaceDims); + + /// @brief Check if the state is initialized. + /// @return True if the state is initialized, false otherwise. + bool is_initialized() const; + + /// @brief Check if the state is a density matrix. + /// @return True if the state is a density matrix, false otherwise. + bool is_density_matrix() const; + + /// @brief Dump the state data to a string for debugging purposes. + /// @return String representation of the state data. + std::string dump() const; + + /// @brief Convert the state vector to a density matrix. + /// @return A new cudm_mat_state representing the density matrix. + cudm_mat_state to_density_matrix() const; + + /// @brief Get the underlying implementation (if any). + /// @return The underlying state implementation. + cudensitymatState_t get_impl() const; + + /// @brief Attach raw data to the internal state representation + void attach_storage(); + +private: + std::vector> rawData_; + cudensitymatState_t state_; + cudensitymatHandle_t handle_; + std::vector hilbertSpaceDims_; + + /// @brief Calculate the size of the state vector for the given Hilbert space + /// dimensions. + /// @param hilbertSpaceDims Hilbert space dimensions. + /// @return Size of the state vector. + size_t calculate_state_vector_size( + const std::vector &hilbertSpaceDims) const; + + /// @brief Calculate the size of the density matrix for the given Hilbert + /// space dimensions. + /// @param hilbertSpaceDims Hilbert space dimensions. + /// @return Size of the density matrix. + size_t calculate_density_matrix_size( + const std::vector &hilbertSpaceDims) const; +}; + +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 283e77e7ff..90354c7e96 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp new file mode 100644 index 0000000000..dfbac25057 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include + +namespace cudaq { + +cudm_mat_state::cudm_mat_state(std::vector> rawData) + : rawData_(rawData), state_(nullptr), handle_(nullptr), + hilbertSpaceDims_() { + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); +} + +cudm_mat_state::~cudm_mat_state() { + if (state_) { + cudensitymatDestroyState(state_); + } + if (handle_) { + cudensitymatDestroy(handle_); + } +} + +void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { + if (state_) { + throw std::runtime_error("State is already initialized."); + } + + hilbertSpaceDims_ = hilbertSpaceDims; + cudensitymatStatePurity_t purity; + + if (rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims)) { + purity = CUDENSITYMAT_STATE_PURITY_MIXED; + } else if (rawData_.size() == calculate_state_vector_size(hilbertSpaceDims)) { + purity = CUDENSITYMAT_STATE_PURITY_PURE; + } else { + throw std::invalid_argument( + "Invalid rawData size for the given Hilbert space dimensions."); + } + + HANDLE_CUDM_ERROR(cudensitymatCreateState( + handle_, purity, static_cast(hilbertSpaceDims.size()), + hilbertSpaceDims.data(), 1, CUDA_C_64F, &state_)); + + attach_storage(); +} + +bool cudm_mat_state::is_initialized() const { return state_ != nullptr; } + +bool cudm_mat_state::is_density_matrix() const { + if (!is_initialized()) { + return false; + } + + return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); +} + +std::string cudm_mat_state::dump() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + std::ostringstream oss; + oss << "State data: ["; + for (size_t i = 0; i < rawData_.size(); i++) { + oss << rawData_[i]; + if (i < rawData_.size() - 1) { + oss << ", "; + } + } + oss << "]"; + return oss.str(); +} + +cudm_mat_state cudm_mat_state::to_density_matrix() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + if (is_density_matrix()) { + throw std::runtime_error("State is already a density matrix."); + } + + std::vector> densityMatrix; + size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims_); + size_t expectedDensityMatrixSize = vectorSize * vectorSize; + densityMatrix.resize(expectedDensityMatrixSize); + + for (size_t i = 0; i < vectorSize; i++) { + for (size_t j = 0; j < vectorSize; j++) { + densityMatrix[i * vectorSize + j] = rawData_[i] * std::conj(rawData_[j]); + } + } + + cudm_mat_state densityMatrixState(densityMatrix); + densityMatrixState.init_state(hilbertSpaceDims_); + return densityMatrixState; +} + +cudensitymatState_t cudm_mat_state::get_impl() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + return state_; +} + +void cudm_mat_state::attach_storage() { + if (!state_) { + throw std::runtime_error("State is not initialized."); + } + + if (rawData_.empty()) { + throw std::runtime_error("Raw data is empty. Cannot attach storage."); + } + + // Retrieve the number of state components + int32_t numStateComponents; + HANDLE_CUDM_ERROR( + cudensitymatStateGetNumComponents(handle_, state_, &numStateComponents)); + + // Retrieve the storage size for each component + std::vector componentBufferSizes(numStateComponents); + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + handle_, state_, numStateComponents, componentBufferSizes.data())); + + // Validate that rawData_ has sufficient space for all components + size_t totalSize = 0; + for (size_t size : componentBufferSizes) { + totalSize += size; + } + if (rawData_.size() * sizeof(std::complex) < totalSize) { + throw std::invalid_argument( + "Raw data size is insufficient to cover all components."); + } + + // Attach storage for each component + std::vector componentBuffers(numStateComponents); + std::vector *> rawComponentData(numStateComponents); + + size_t offset = 0; + for (int32_t i = 0; i < numStateComponents; i++) { + rawComponentData[i] = + reinterpret_cast *>(rawData_.data()) + offset; + componentBuffers[i] = static_cast(rawComponentData[i]); + offset += componentBufferSizes[i] / sizeof(std::complex); + } + + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + handle_, state_, numStateComponents, componentBuffers.data(), + componentBufferSizes.data())); +} + +size_t cudm_mat_state::calculate_state_vector_size( + const std::vector &hilbertSpaceDims) const { + size_t size = 1; + for (auto dim : hilbertSpaceDims) { + size *= dim; + } + return size; +} + +size_t cudm_mat_state::calculate_density_matrix_size( + const std::vector &hilbertSpaceDims) const { + size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); + return vectorSize * vectorSize; +} +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_state.h b/runtime/cudaq/dynamics/cudm_state.h deleted file mode 100644 index 1f20f9483f..0000000000 --- a/runtime/cudaq/dynamics/cudm_state.h +++ /dev/null @@ -1,28 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include -#include - -namespace cudaq { -class cudm_mat_state { -public: - cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, - int num_modes, const std::vector &mode_extents); - ~cudm_mat_state(); - - void scale(double factor, cudaStream_t stream); - cudensitymatState_t get() const; - -private: - cudensitymatState_t state; - cudensitymatHandle_t handle; -}; -} // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f87a25d1ec..430fac8ca3 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -54,6 +54,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp dynamics/test_cudm_helpers.cpp + dynamics/test_cudm_state.cpp ) # Make it so we can get function symbols @@ -281,6 +282,7 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp dynamics/test_cudm_helpers.cpp + dynamics/test_cudm_state.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp new file mode 100644 index 0000000000..237142fcd0 --- /dev/null +++ b/unittests/dynamics/test_cudm_state.cpp @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace cudaq; + +class CuDensityMatStateTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data for a single 2-qubit system + hilbertSpaceDims = {2, 2}; + + // State vector (pure state) for |00> + stateVectorData = { + std::complex(1.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0)}; + + // Density matrix for |00><00| + densityMatrixData = { + std::complex(1.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0)}; + } + + void TearDown() override {} + + std::vector hilbertSpaceDims; + std::vector> stateVectorData; + std::vector> densityMatrixData; +}; + +TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { + cudm_mat_state state(stateVectorData); + EXPECT_FALSE(state.is_initialized()); + + EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); + EXPECT_TRUE(state.is_initialized()); + EXPECT_FALSE(state.is_density_matrix()); + + EXPECT_NO_THROW(state.dump()); +} + +TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { + cudm_mat_state state(densityMatrixData); + EXPECT_FALSE(state.is_initialized()); + + EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); + EXPECT_TRUE(state.is_initialized()); + EXPECT_TRUE(state.is_density_matrix()); + + EXPECT_NO_THROW(state.dump()); +} + +TEST_F(CuDensityMatStateTest, InvalidInitialization) { + // Data size mismatch for hilbertSpaceDims (2x2 system expects size 4 or 16) + std::vector> invalidData = { + std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; + + cudm_mat_state state(invalidData); + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); +} + +TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { + cudm_mat_state state(stateVectorData); + state.init_state(hilbertSpaceDims); + + EXPECT_FALSE(state.is_density_matrix()); + + cudm_mat_state densityMatrixState = state.to_density_matrix(); + + EXPECT_TRUE(densityMatrixState.is_density_matrix()); + EXPECT_TRUE(densityMatrixState.is_initialized()); + + EXPECT_NO_THROW(densityMatrixState.dump()); +} + +TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { + cudm_mat_state state(densityMatrixData); + state.init_state(hilbertSpaceDims); + + EXPECT_TRUE(state.is_density_matrix()); + EXPECT_THROW(state.to_density_matrix(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, DumpUninitializedState) { + cudm_mat_state state(stateVectorData); + EXPECT_THROW(state.dump(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.attach_storage(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, DestructorCleansUp) { + cudm_mat_state state(stateVectorData); + + EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); +} From d1acad380606a073b387e341510eb431455716fa Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 25 Jan 2025 11:57:24 -0800 Subject: [PATCH 027/311] Addign few more unit tests for cudm_state Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_state.cpp | 20 ++++++-- unittests/dynamics/test_cudm_state.cpp | 68 ++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index dfbac25057..b456bef45d 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -33,15 +33,25 @@ void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { } hilbertSpaceDims_ = hilbertSpaceDims; + + size_t rawDataSize = rawData_.size(); + size_t expectedDensityMatrixSize = + calculate_density_matrix_size(hilbertSpaceDims); + size_t expectedStateVectorSize = + calculate_state_vector_size(hilbertSpaceDims); + + if (rawDataSize != expectedDensityMatrixSize && + rawDataSize != expectedStateVectorSize) { + throw std::invalid_argument( + "Invalid rawData size for the given Hilbert space dimensions."); + } + cudensitymatStatePurity_t purity; - if (rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims)) { + if (rawDataSize == expectedDensityMatrixSize) { purity = CUDENSITYMAT_STATE_PURITY_MIXED; - } else if (rawData_.size() == calculate_state_vector_size(hilbertSpaceDims)) { + } else if (rawDataSize == expectedStateVectorSize) { purity = CUDENSITYMAT_STATE_PURITY_PURE; - } else { - throw std::invalid_argument( - "Invalid rawData size for the given Hilbert space dimensions."); } HANDLE_CUDM_ERROR(cudensitymatCreateState( diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 237142fcd0..d8ef07ffc4 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -115,3 +115,71 @@ TEST_F(CuDensityMatStateTest, DestructorCleansUp) { EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); } + +TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { + std::vector> emptyData; + cudm_mat_state state(emptyData); + + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); +} + +TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { + hilbertSpaceDims = {2}; + stateVectorData = {std::complex(1.0, 0.0), + std::complex(0.0, 0.0)}; + cudm_mat_state state(stateVectorData); + + state.init_state(hilbertSpaceDims); + + EXPECT_FALSE(state.is_density_matrix()); + + cudm_mat_state densityMatrixState = state.to_density_matrix(); + EXPECT_TRUE(densityMatrixState.is_density_matrix()); + EXPECT_TRUE(densityMatrixState.is_initialized()); + + EXPECT_NO_THROW(densityMatrixState.dump()); +} + +TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { + // 3x3 space is not supported by the provided rawData size + hilbertSpaceDims = {3, 3}; + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); +} + +TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.to_density_matrix(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, MultipleInitialization) { + cudm_mat_state state(stateVectorData); + state.init_state(hilbertSpaceDims); + + EXPECT_TRUE(state.is_initialized()); + + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { + cudm_mat_state state(densityMatrixData); + state.init_state(hilbertSpaceDims); + + EXPECT_TRUE(state.is_density_matrix()); + EXPECT_TRUE(state.is_initialized()); +} + +TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { + cudm_mat_state state(stateVectorData); + state.init_state(hilbertSpaceDims); + + EXPECT_NO_THROW(state.dump()); +} + +TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.dump(), std::runtime_error); +} From 7ad8c8267a6cacfeae0104bc960df355c0b15ee0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 25 Jan 2025 15:04:22 -0800 Subject: [PATCH 028/311] Adding an enum with initial quantum state and initilize_state based on InitialStateArgT Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 25 +++++++++++++++++++++++++ runtime/cudaq/helpers.h | 11 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index e6c50d14a0..0c03a81d97 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/helpers.h" +#include #include #include @@ -144,4 +145,28 @@ OperatorHelpers::canonicalize_degrees(const std::vector °rees) { std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); return sorted_degrees; } + +// Initialize state based on InitialStateArgT +// TODO: Implement quantum state initialization +void OperatorHelpers::initialize_state(const InitialStateArgT &stateArg, + const std::vector &dimensions) { + if (std::holds_alternative(stateArg)) { + InitialState initState = std::get(stateArg); + switch (initState) { + case InitialState::ZERO: + // ZERO quantum state initialization + std::cout << "Initialized to ZERO state." << std::endl; + break; + case InitialState::UNIFORM: + // UNIFORM quantum state initialization + std::cout << "Initialized to UNIFORM state." << std::endl; + break; + } + } else if (std::holds_alternative(stateArg)) { + // Initialization from runtime state + std::cout << "Initialized using runtime state." << std::endl; + } else { + throw std::invalid_argument("Unsupported InitialStateArgT type."); + } +} } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 52bb131d0c..39f73f7969 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -15,9 +15,16 @@ #include #include #include +#include #include namespace cudaq { + +// Enum to specify the initial quantum state. +enum class InitialState { ZERO, UNIFORM }; + +using InitialStateArgT = std::variant; + class OperatorHelpers { public: // Aggregate parameters from multiple mappings. @@ -46,5 +53,9 @@ class OperatorHelpers { // Canonicalize degrees by sorting in descending order. static std::vector canonicalize_degrees(const std::vector °rees); + + // Initialize state based on InitialStateArgT. + static void initialize_state(const InitialStateArgT &stateArg, + const std::vector &dimensions); }; } // namespace cudaq \ No newline at end of file From 97c337d7de0c4e300be6c2a04fb309ceb3b81ba6 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 25 Jan 2025 16:03:10 -0800 Subject: [PATCH 029/311] Fixing spelling Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 4 ++-- runtime/cudaq/operators.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 7b5f717806..85379f998a 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -53,7 +53,7 @@ class Evolution { const std::vector>> &schedule_parameters); - /// Evolves a single quantum state under a given hamiltonian. + /// Evolves a single quantum state under a given `hamiltonian`. static evolve_result evolve_single(const operator_sum &hamiltonian, const std::map &dimensions, @@ -64,7 +64,7 @@ class Evolution { std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); - /// Evolves a single or multiple quantum states under a given hamiltonian. + /// Evolves a single or multiple quantum states under a given `hamiltonian`. /// Run only for dynamics target else throw error static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index fe088dbd79..5f07f5dd49 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -473,12 +473,12 @@ class rydberg_hamiltonian : public operator_sum { /// @brief Constructor. /// @param atom_sites List of 2D coordinates for trap sites. - /// @param amplitude Time-dependant driving amplitude, Omega(t). - /// @param phase Time-dependant driving phase, phi(t). - /// @param delta_global Time-dependant driving detuning, Delta_global(t). + /// @param amplitude Time-dependent driving amplitude, Omega(t). + /// @param phase Time-dependent driving phase, phi(t). + /// @param delta_global Time-dependent driving detuning, Delta_global(t). /// @param atom_filling Optional. Marks occupied trap sites (1) and empty /// sites (0). Defaults to all sites occupied. - /// @param delta_local Optional. A tuple of Delta_local(t) and site dependant + /// @param delta_local Optional. A tuple of Delta_local(t) and site dependent /// local detuning factors. rydberg_hamiltonian( const std::vector &atom_sites, From 83b2d632993e30d04f663effdc5823391088d651 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 27 Jan 2025 16:09:57 -0800 Subject: [PATCH 030/311] * Renaming cudm_mat_state -> cudm_state * Adding create_initial_state method to create initial state based on the passed InitialStateArgT Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 25 ++++++-- runtime/cudaq/dynamics/cudm_state.cpp | 85 +++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 814a7ad342..e7f858c8b7 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -16,13 +16,28 @@ #include namespace cudaq { -class cudm_mat_state { +// Enum to specify the initial quantum state. +enum class InitialState { ZERO, UNIFORM }; + +using InitialStateArgT = std::variant; + +class cudm_state { public: /// @brief To initialize state with raw data. - explicit cudm_mat_state(std::vector> rawData); + explicit cudm_state(std::vector> rawData); /// @brief Destructor to clean up resources - ~cudm_mat_state(); + ~cudm_state(); + + /// @brief Factory method to create an initial state. + /// @param InitialStateArgT The type or representation of the initial state. + /// @param Dimensions of the Hilbert space. + /// @param hasCollapseOps Whether collapse operators are present. + /// @return A new 'cudm_state' initialized to the specified state. + static cudm_state + create_initial_state(const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, + bool hasCollapseOps); /// @brief Initialize the state as a density matrix or state vector based on /// dimensions. @@ -42,8 +57,8 @@ class cudm_mat_state { std::string dump() const; /// @brief Convert the state vector to a density matrix. - /// @return A new cudm_mat_state representing the density matrix. - cudm_mat_state to_density_matrix() const; + /// @return A new cudm_state representing the density matrix. + cudm_state to_density_matrix() const; /// @brief Get the underlying implementation (if any). /// @return The underlying state implementation. diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index b456bef45d..40d6d49796 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -6,19 +6,22 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include #include #include +#include #include +#include namespace cudaq { -cudm_mat_state::cudm_mat_state(std::vector> rawData) +cudm_state::cudm_state(std::vector> rawData) : rawData_(rawData), state_(nullptr), handle_(nullptr), hilbertSpaceDims_() { HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); } -cudm_mat_state::~cudm_mat_state() { +cudm_state::~cudm_state() { if (state_) { cudensitymatDestroyState(state_); } @@ -27,7 +30,7 @@ cudm_mat_state::~cudm_mat_state() { } } -void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { +void cudm_state::init_state(const std::vector &hilbertSpaceDims) { if (state_) { throw std::runtime_error("State is already initialized."); } @@ -61,9 +64,9 @@ void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { attach_storage(); } -bool cudm_mat_state::is_initialized() const { return state_ != nullptr; } +bool cudm_state::is_initialized() const { return state_ != nullptr; } -bool cudm_mat_state::is_density_matrix() const { +bool cudm_state::is_density_matrix() const { if (!is_initialized()) { return false; } @@ -71,7 +74,7 @@ bool cudm_mat_state::is_density_matrix() const { return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); } -std::string cudm_mat_state::dump() const { +std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); } @@ -88,7 +91,7 @@ std::string cudm_mat_state::dump() const { return oss.str(); } -cudm_mat_state cudm_mat_state::to_density_matrix() const { +cudm_state cudm_state::to_density_matrix() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); } @@ -108,19 +111,19 @@ cudm_mat_state cudm_mat_state::to_density_matrix() const { } } - cudm_mat_state densityMatrixState(densityMatrix); + cudm_state densityMatrixState(densityMatrix); densityMatrixState.init_state(hilbertSpaceDims_); return densityMatrixState; } -cudensitymatState_t cudm_mat_state::get_impl() const { +cudensitymatState_t cudm_state::get_impl() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); } return state_; } -void cudm_mat_state::attach_storage() { +void cudm_state::attach_storage() { if (!state_) { throw std::runtime_error("State is not initialized."); } @@ -166,7 +169,7 @@ void cudm_mat_state::attach_storage() { componentBufferSizes.data())); } -size_t cudm_mat_state::calculate_state_vector_size( +size_t cudm_state::calculate_state_vector_size( const std::vector &hilbertSpaceDims) const { size_t size = 1; for (auto dim : hilbertSpaceDims) { @@ -175,9 +178,67 @@ size_t cudm_mat_state::calculate_state_vector_size( return size; } -size_t cudm_mat_state::calculate_density_matrix_size( +size_t cudm_state::calculate_density_matrix_size( const std::vector &hilbertSpaceDims) const { size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); return vectorSize * vectorSize; } + +// Initialize state based on InitialStateArgT +cudm_state +cudm_state::create_initial_state(const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, + bool hasCollapseOps) { + size_t stateVectorSize = + std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), + static_cast(1), std::multiplies<>{}); + + std::vector> rawData; + + if (std::holds_alternative(initialStateArg)) { + InitialState initialState = std::get(initialStateArg); + + if (initialState == InitialState::ZERO) { + rawData.resize(stateVectorSize, {0.0, 0.0}); + // |0> state + rawData[0] = {1.0, 0.0}; + } else if (initialState == InitialState::UNIFORM) { + rawData.resize(stateVectorSize, {1.0 / std::sqrt(stateVectorSize), 0.0}); + } else { + throw std::invalid_argument("Unsupported InitialState type."); + } + } else if (std::holds_alternative(initialStateArg)) { + void *runtimeState = std::get(initialStateArg); + if (!runtimeState) { + throw std::invalid_argument("Runtime state pointer is null."); + } + + try { + auto *externalData = + reinterpret_cast> *>(runtimeState); + + if (!externalData || externalData->empty()) { + throw std::invalid_argument( + "Runtime state contains invalid or empty data."); + } + + rawData = *externalData; + } catch (const std::exception &e) { + throw std::runtime_error("Failed to interpret runtime state: " + + std::string(e.what())); + } + } else { + throw std::invalid_argument("Unsupported InitialStateArgT type."); + } + + cudm_state state(rawData); + state.init_state(hilbertSpaceDims); + + // Convert to a density matrix if collapse operators are present. + if (hasCollapseOps && !state.is_density_matrix()) { + state = state.to_density_matrix(); + } + + return state; +} } // namespace cudaq From ad6e5eb65da48b522d70383b702e621da6691c82 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 27 Jan 2025 17:07:25 -0800 Subject: [PATCH 031/311] Exposing state's rawData to be used in the stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 4 ++++ runtime/cudaq/dynamics/cudm_state.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index e7f858c8b7..0425e6db7c 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -67,6 +67,10 @@ class cudm_state { /// @brief Attach raw data to the internal state representation void attach_storage(); + /// @brief Get a copy of the raw data representing the quantum state. + /// @return A copy of the raw data as a vector of complex numbers. + std::vector> get_raw_data() const; + private: std::vector> rawData_; cudensitymatState_t state_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 40d6d49796..11b643d177 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -74,6 +74,10 @@ bool cudm_state::is_density_matrix() const { return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); } +std::vector> cudm_state::get_raw_data() const { + return rawData_; +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From a5415fcf6b5f9aed1f5538069760f862c203dfd0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 28 Jan 2025 15:02:31 -0800 Subject: [PATCH 032/311] Adding cudm_time_stepper with unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_solver.h | 52 +++++++++++ runtime/cudaq/cudm_state.h | 4 + runtime/cudaq/cudm_time_stepper.h | 26 ++++++ runtime/cudaq/dynamics/CMakeLists.txt | 2 +- runtime/cudaq/dynamics/cudm_solver.cpp | 52 +++++++++++ runtime/cudaq/dynamics/cudm_state.cpp | 4 + runtime/cudaq/dynamics/cudm_time_stepper.cpp | 57 ++++++++++++ runtime/cudaq/dynamics/helpers.cpp | 25 +----- runtime/cudaq/helpers.h | 12 +-- unittests/CMakeLists.txt | 3 +- unittests/dynamics/test_cudm_state.cpp | 36 ++++---- unittests/dynamics/test_cudm_time_stepper.cpp | 86 +++++++++++++++++++ 12 files changed, 303 insertions(+), 56 deletions(-) create mode 100644 runtime/cudaq/cudm_solver.h create mode 100644 runtime/cudaq/cudm_time_stepper.h create mode 100644 runtime/cudaq/dynamics/cudm_solver.cpp create mode 100644 runtime/cudaq/dynamics/cudm_time_stepper.cpp create mode 100644 unittests/dynamics/test_cudm_time_stepper.cpp diff --git a/runtime/cudaq/cudm_solver.h b/runtime/cudaq/cudm_solver.h new file mode 100644 index 0000000000..2c7e14bfca --- /dev/null +++ b/runtime/cudaq/cudm_solver.h @@ -0,0 +1,52 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "runtime/common/EvolveResult.h" +#include +#include +#include + +namespace cudaq { + +// Configuration struct for the solver +struct Config { + std::map dimensions; // Hilbert space dimensions + operator_sum hamiltonian; // Hamiltonian operator + std::vector collapse_operators; // Collapse operators + std::vector observables; // Observables to evaluate + std::variant>> initial_state; // Initial state + Schedule schedule; // Evolution schedule + bool store_intermediate_results = false; // Flag to store intermediate states +}; + +class cudm_solver { +public: + cudm_solver(const Config &config); + + void validate_config(); + + cudm_state initialize_state(); + + void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &obervable_ops, evolve_result &result); + + evolve_result evolve_dynamics(); + + cudensitymatOperator_t construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve); + +private: + Config config_; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 0425e6db7c..ad7eb22cae 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -71,6 +71,10 @@ class cudm_state { /// @return A copy of the raw data as a vector of complex numbers. std::vector> get_raw_data() const; + /// @brief Get a copy of the hilbert space dimensions for the quantum state. + /// @return A copy of the hilbert space dimensions of a vector of integers. + std::vector get_hilbert_space_dims() const; + private: std::vector> rawData_; cudensitymatState_t state_; diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/cudaq/cudm_time_stepper.h new file mode 100644 index 0000000000..59b0d942d8 --- /dev/null +++ b/runtime/cudaq/cudm_time_stepper.h @@ -0,0 +1,26 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/base_time_stepper.h" +#include "cudaq/cudm_state.h" +#include + +namespace cudaq { +class cudm_time_stepper : public BaseTimeStepper { +public: + explicit cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian); + + void compute(cudm_state &state, double t, double step_size) override; + +private: + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 90354c7e96..636d8b4096 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp cudm_time_stepper.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/cudaq/dynamics/cudm_solver.cpp new file mode 100644 index 0000000000..4f76d4afc8 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_solver.cpp @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_solver.h" +#include "cudaq/cudm_helpers.h" +#include "cudaq/cudm_state.h" +#include "cudaq/base_time_stepper.h" + +namespace cudaq { +cudm_solver::cudm_solver(const Config &config) : config_(config) { + validate_config(); +} + +void cudm_solver::validate_config() { + if (config_.dimensions.empty()) { + throw std::invalid_argument("Dimensions map cannot be empty."); + } + + if (config_.hamiltonian.get_terms().empty()) { + throw std::invalid_argument("Hamiltonian must have at least one term."); + } + + if (config_.dimensions.empty()) { + throw std::invalid_argument("Schedule cannot be empty."); + } +} + +cudm_state cudm_solver::initialize_state() { + std::vector mode_extents; + for (const auto &[key, value] : config_.dimensions) { + mode_extents.push_back(value); + } + + return cudm_state::create_initial_state(config_.initial_state, mode_extents, !config_.collapse_operators.empty()); +} + +cudensitymatOperator_t cudm_solver::construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve) { + return construct_liovillian(handle, hamiltonian, collapse_operators, me_solve ? 1.0 : 0.0); +} + +void cudm_solver::evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &observable_ops, evolve_result &result) { + auto handle = state.get_impl(); + + // Initialize the stepper + BaseTimeStepper timeStepper(liouvillian, handle); +} +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 11b643d177..1e84683b0e 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -78,6 +78,10 @@ std::vector> cudm_state::get_raw_data() const { return rawData_; } +std::vector cudm_state::get_hilbert_space_dims() const { + return hilbertSpaceDims_; +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp new file mode 100644 index 0000000000..86de154442 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_time_stepper.h" +#include "cudaq/cudm_error_handling.h" +#include + +namespace cudaq { +cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) : handle_(handle), liouvillian_(liouvillian) {} + +void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { + if (!state.is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + std::cout << "Preparing workspace ..." << std::endl; + // Prepare workspace + cudensitymatWorkspaceDescriptor_t workspace; + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); + if (!workspace) { + throw std::runtime_error("Failed to create workspace for the operator."); + } + + std::cout << "Create a new state for the next step ..." << std::endl; + // Create a new state for the next step + cudm_state next_state(state.to_density_matrix().get_raw_data()); + next_state.init_state(state.get_hilbert_space_dims()); + + if (!next_state.is_initialized()) { + throw std::runtime_error("Next state failed to initialize."); + } + + if (!handle_) { + throw std::runtime_error("cudm_time_stepper handle is not initializes."); + } + + if (!liouvillian_) { + throw std::runtime_error("Liouvillian is not initialized."); + } + + std::cout << "cudensitymatOperatorComputeAction ..." << std::endl; + HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction(handle_, liouvillian_, t, 0, nullptr, state.get_impl(), next_state.get_impl(), workspace, 0)); + + std::cout << "Update the state ..." << std::endl; + // Update the state + state = std::move(next_state); + + std::cout << "Clean up workspace ..." << std::endl; + // Clean up workspace + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); +} +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 0c03a81d97..ae455098f5 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -146,27 +146,4 @@ OperatorHelpers::canonicalize_degrees(const std::vector °rees) { return sorted_degrees; } -// Initialize state based on InitialStateArgT -// TODO: Implement quantum state initialization -void OperatorHelpers::initialize_state(const InitialStateArgT &stateArg, - const std::vector &dimensions) { - if (std::holds_alternative(stateArg)) { - InitialState initState = std::get(stateArg); - switch (initState) { - case InitialState::ZERO: - // ZERO quantum state initialization - std::cout << "Initialized to ZERO state." << std::endl; - break; - case InitialState::UNIFORM: - // UNIFORM quantum state initialization - std::cout << "Initialized to UNIFORM state." << std::endl; - break; - } - } else if (std::holds_alternative(stateArg)) { - // Initialization from runtime state - std::cout << "Initialized using runtime state." << std::endl; - } else { - throw std::invalid_argument("Unsupported InitialStateArgT type."); - } -} -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 39f73f7969..48e7454adf 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -19,12 +19,6 @@ #include namespace cudaq { - -// Enum to specify the initial quantum state. -enum class InitialState { ZERO, UNIFORM }; - -using InitialStateArgT = std::variant; - class OperatorHelpers { public: // Aggregate parameters from multiple mappings. @@ -53,9 +47,5 @@ class OperatorHelpers { // Canonicalize degrees by sorting in descending order. static std::vector canonicalize_degrees(const std::vector °rees); - - // Initialize state based on InitialStateArgT. - static void initialize_state(const InitialStateArgT &stateArg, - const std::vector &dimensions); }; -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 430fac8ca3..74c404b49b 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -55,6 +55,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/rydberg_hamiltonian.cpp dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp + dynamics/test_cudm_time_stepper.cpp ) # Make it so we can get function symbols @@ -281,8 +282,6 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_ops_simple.cpp dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp - dynamics/test_cudm_helpers.cpp - dynamics/test_cudm_state.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index d8ef07ffc4..c5e0e264cf 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -47,7 +47,7 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -58,7 +58,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_mat_state state(densityMatrixData); + cudm_state state(densityMatrixData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -73,17 +73,17 @@ TEST_F(CuDensityMatStateTest, InvalidInitialization) { std::vector> invalidData = { std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_mat_state state(invalidData); + cudm_state state(invalidData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); - cudm_mat_state densityMatrixState = state.to_density_matrix(); + cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); @@ -92,7 +92,7 @@ TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_mat_state state(densityMatrixData); + cudm_state state(densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -100,25 +100,25 @@ TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, DumpUninitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.attach_storage(), std::runtime_error); } TEST_F(CuDensityMatStateTest, DestructorCleansUp) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - cudm_mat_state state(emptyData); + cudm_state state(emptyData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } @@ -127,13 +127,13 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; stateVectorData = {std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); - cudm_mat_state densityMatrixState = state.to_density_matrix(); + cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); @@ -143,19 +143,19 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); } TEST_F(CuDensityMatStateTest, MultipleInitialization) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_initialized()); @@ -164,7 +164,7 @@ TEST_F(CuDensityMatStateTest, MultipleInitialization) { } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_mat_state state(densityMatrixData); + cudm_state state(densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -172,14 +172,14 @@ TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp new file mode 100644 index 0000000000..d0693fec18 --- /dev/null +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include +#include +#include + +using namespace cudaq; + +// Mock Liouvillian operator creation +cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { + cudensitymatOperator_t liouvillian; + std::vector dimensions = {2, 2}; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, static_cast(dimensions.size()), dimensions.data(), &liouvillian)); + return liouvillian; +} + +// Mock Hilbert space dimensions +std::vector> mock_initial_state_data() { + return { + {1.0, 0.0}, {0.0, 0.0}, + {0.0, 0.0}, {0.0, 0.0} + }; +} + +// Mock initial raw state data +std::vector mock_hilbert_space_dims() { + return {2, 2}; +} + +class CuDensityMatTimeStepperTest : public ::testing::Test { +protected: + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; + cudm_time_stepper *time_stepper_; + cudm_state state_; + + CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; + + void SetUp() override { + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + + // Create a mock Liouvillian + liouvillian_ = mock_liouvillian(handle_); + + // Initialize the time stepper + time_stepper_ = new cudm_time_stepper(liouvillian_, handle_); + + // Initialize the state + state_.init_state(mock_hilbert_space_dims()); + + ASSERT_TRUE(state_.is_initialized()); + } + + void TearDown() override { + // Clean up + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + delete time_stepper_; + } +}; + +// Test initialization of cudm_time_stepper +TEST_F(CuDensityMatTimeStepperTest, Initialization) { + ASSERT_NE(time_stepper_, nullptr); + ASSERT_TRUE(state_.is_initialized()); + ASSERT_FALSE(state_.is_density_matrix()); +} + +// Test a single compute step +TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { + ASSERT_TRUE(state_.is_initialized()); + EXPECT_NO_THROW(time_stepper_->compute(state_, 0.0, 1.0)); + ASSERT_TRUE(state_.is_initialized()); +} + + + From dd5ebb08e2ccbfa4291ae446e3a4f1e8f6257a41 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 29 Jan 2025 16:00:45 -0800 Subject: [PATCH 033/311] Refactoring time_stepper compute method to align with cuquantum workflow Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 6 + runtime/cudaq/cudm_solver.h | 50 ++++--- runtime/cudaq/cudm_state.h | 11 +- runtime/cudaq/cudm_time_stepper.h | 11 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 21 +++ runtime/cudaq/dynamics/cudm_solver.cpp | 54 ++++--- runtime/cudaq/dynamics/cudm_state.cpp | 54 +++---- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 135 ++++++++++++------ runtime/cudaq/dynamics/helpers.cpp | 1 + unittests/dynamics/test_cudm_state.cpp | 38 ++--- unittests/dynamics/test_cudm_time_stepper.cpp | 93 ++++++------ 11 files changed, 288 insertions(+), 186 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index c2968229fb..08e7a6c7d3 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -40,4 +40,10 @@ cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma); + +// Function for creating an array copy in GPU memory +void *create_array_gpu(const std::vector> &cpu_array); + +// Function to detsroy a previously created array copy in GPU memory +void destroy_array_gpu(void *gpu_array); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/cudm_solver.h b/runtime/cudaq/cudm_solver.h index 2c7e14bfca..32fc7b00a8 100644 --- a/runtime/cudaq/cudm_solver.h +++ b/runtime/cudaq/cudm_solver.h @@ -8,45 +8,51 @@ #pragma once -#include -#include -#include -#include -#include -#include #include "runtime/common/EvolveResult.h" -#include #include +#include +#include +#include +#include +#include +#include #include +#include namespace cudaq { // Configuration struct for the solver struct Config { - std::map dimensions; // Hilbert space dimensions - operator_sum hamiltonian; // Hamiltonian operator - std::vector collapse_operators; // Collapse operators - std::vector observables; // Observables to evaluate - std::variant>> initial_state; // Initial state - Schedule schedule; // Evolution schedule - bool store_intermediate_results = false; // Flag to store intermediate states + std::map dimensions; // Hilbert space dimensions + operator_sum hamiltonian; // Hamiltonian operator + std::vector collapse_operators; // Collapse operators + std::vector observables; // Observables to evaluate + std::variant>> + initial_state; // Initial state + Schedule schedule; // Evolution schedule + bool store_intermediate_results = false; // Flag to store intermediate states }; class cudm_solver { public: - cudm_solver(const Config &config); + cudm_solver(const Config &config); - void validate_config(); + void validate_config(); - cudm_state initialize_state(); + cudm_state initialize_state(); - void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &obervable_ops, evolve_result &result); + void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, + const std::vector &obervable_ops, + evolve_result &result); - evolve_result evolve_dynamics(); + evolve_result evolve_dynamics(); - cudensitymatOperator_t construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve); + cudensitymatOperator_t construct_liouvillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + bool me_solve); private: - Config config_; + Config config_; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index ad7eb22cae..220136d1f8 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -24,7 +24,8 @@ using InitialStateArgT = std::variant; class cudm_state { public: /// @brief To initialize state with raw data. - explicit cudm_state(std::vector> rawData); + explicit cudm_state(cudensitymatHandle_t handle, + std::vector> rawData); /// @brief Destructor to clean up resources ~cudm_state(); @@ -34,10 +35,9 @@ class cudm_state { /// @param Dimensions of the Hilbert space. /// @param hasCollapseOps Whether collapse operators are present. /// @return A new 'cudm_state' initialized to the specified state. - static cudm_state - create_initial_state(const InitialStateArgT &initialStateArg, - const std::vector &hilbertSpaceDims, - bool hasCollapseOps); + static cudm_state create_initial_state( + cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, bool hasCollapseOps); /// @brief Initialize the state as a density matrix or state vector based on /// dimensions. @@ -77,6 +77,7 @@ class cudm_state { private: std::vector> rawData_; + std::complex *gpuData_; cudensitymatState_t state_; cudensitymatHandle_t handle_; std::vector hilbertSpaceDims_; diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/cudaq/cudm_time_stepper.h index 59b0d942d8..a245b42c80 100644 --- a/runtime/cudaq/cudm_time_stepper.h +++ b/runtime/cudaq/cudm_time_stepper.h @@ -15,12 +15,13 @@ namespace cudaq { class cudm_time_stepper : public BaseTimeStepper { public: - explicit cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian); + explicit cudm_time_stepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian); - void compute(cudm_state &state, double t, double step_size) override; + void compute(cudm_state &state, double t, double step_size) override; private: - cudensitymatHandle_t handle_; - cudensitymatOperator_t liouvillian_; + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 999ba90ff5..6325189452 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -268,4 +268,25 @@ cudensitymatOperator_t construct_liovillian( throw; } } + +// Function for creating an array copy in GPU memory +void *create_array_gpu(const std::vector> &cpu_array) { + void *gpu_array{nullptr}; + const std::size_t array_size = + cpu_array.size() * sizeof(std::complex); + if (array_size > 0) { + HANDLE_CUDA_ERROR(cudaMalloc(&gpu_array, array_size)); + HANDLE_CUDA_ERROR(cudaMemcpy(gpu_array, + static_cast(cpu_array.data()), + array_size, cudaMemcpyHostToDevice)); + } + return gpu_array; +} + +// Function to detsroy a previously created array copy in GPU memory +void destroy_array_gpu(void *gpu_array) { + if (gpu_array) { + HANDLE_CUDA_ERROR(cudaFree(gpu_array)); + } +} } // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/cudaq/dynamics/cudm_solver.cpp index 4f76d4afc8..dc35b7ce97 100644 --- a/runtime/cudaq/dynamics/cudm_solver.cpp +++ b/runtime/cudaq/dynamics/cudm_solver.cpp @@ -7,46 +7,54 @@ ******************************************************************************/ #include "cudaq/cudm_solver.h" +#include "cudaq/base_time_stepper.h" #include "cudaq/cudm_helpers.h" #include "cudaq/cudm_state.h" -#include "cudaq/base_time_stepper.h" namespace cudaq { cudm_solver::cudm_solver(const Config &config) : config_(config) { - validate_config(); + validate_config(); } void cudm_solver::validate_config() { - if (config_.dimensions.empty()) { - throw std::invalid_argument("Dimensions map cannot be empty."); - } + if (config_.dimensions.empty()) { + throw std::invalid_argument("Dimensions map cannot be empty."); + } - if (config_.hamiltonian.get_terms().empty()) { - throw std::invalid_argument("Hamiltonian must have at least one term."); - } + if (config_.hamiltonian.get_terms().empty()) { + throw std::invalid_argument("Hamiltonian must have at least one term."); + } - if (config_.dimensions.empty()) { - throw std::invalid_argument("Schedule cannot be empty."); - } + if (config_.dimensions.empty()) { + throw std::invalid_argument("Schedule cannot be empty."); + } } cudm_state cudm_solver::initialize_state() { - std::vector mode_extents; - for (const auto &[key, value] : config_.dimensions) { - mode_extents.push_back(value); - } + std::vector mode_extents; + for (const auto &[key, value] : config_.dimensions) { + mode_extents.push_back(value); + } - return cudm_state::create_initial_state(config_.initial_state, mode_extents, !config_.collapse_operators.empty()); + return cudm_state::create_initial_state(config_.initial_state, mode_extents, + !config_.collapse_operators.empty()); } -cudensitymatOperator_t cudm_solver::construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve) { - return construct_liovillian(handle, hamiltonian, collapse_operators, me_solve ? 1.0 : 0.0); +cudensitymatOperator_t cudm_solver::construct_liouvillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + bool me_solve) { + return construct_liovillian(handle, hamiltonian, collapse_operators, + me_solve ? 1.0 : 0.0); } -void cudm_solver::evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &observable_ops, evolve_result &result) { - auto handle = state.get_impl(); +void cudm_solver::evolve( + cudm_state &state, cudensitymatOperator_t &liouvillian, + const std::vector &observable_ops, + evolve_result &result) { + auto handle = state.get_impl(); - // Initialize the stepper - BaseTimeStepper timeStepper(liouvillian, handle); + // Initialize the stepper + BaseTimeStepper timeStepper(liouvillian, handle); } -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 1e84683b0e..6d1174dfd1 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -15,18 +15,24 @@ namespace cudaq { -cudm_state::cudm_state(std::vector> rawData) - : rawData_(rawData), state_(nullptr), handle_(nullptr), - hilbertSpaceDims_() { - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); +cudm_state::cudm_state(cudensitymatHandle_t handle, + std::vector> rawData) + : rawData_(rawData), state_(nullptr), handle_(handle), hilbertSpaceDims_() { + // Allocate device memory + size_t dataSize = rawData_.size() * sizeof(std::complex); + cudaMalloc(reinterpret_cast(&gpuData_), dataSize); + + // Copy data from host to device + HANDLE_CUDA_ERROR( + cudaMemcpy(gpuData_, rawData_.data(), dataSize, cudaMemcpyHostToDevice)); } cudm_state::~cudm_state() { if (state_) { cudensitymatDestroyState(state_); } - if (handle_) { - cudensitymatDestroy(handle_); + if (gpuData_) { + cudaFree(gpuData_); } } @@ -119,7 +125,7 @@ cudm_state cudm_state::to_density_matrix() const { } } - cudm_state densityMatrixState(densityMatrix); + cudm_state densityMatrixState(handle_, densityMatrix); densityMatrixState.init_state(hilbertSpaceDims_); return densityMatrixState; } @@ -136,8 +142,9 @@ void cudm_state::attach_storage() { throw std::runtime_error("State is not initialized."); } - if (rawData_.empty()) { - throw std::runtime_error("Raw data is empty. Cannot attach storage."); + if (rawData_.empty() || !gpuData_) { + throw std::runtime_error("Raw data is empty or device memory not " + "allocated. Cannot attach storage."); } // Retrieve the number of state components @@ -150,25 +157,19 @@ void cudm_state::attach_storage() { HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( handle_, state_, numStateComponents, componentBufferSizes.data())); - // Validate that rawData_ has sufficient space for all components - size_t totalSize = 0; - for (size_t size : componentBufferSizes) { - totalSize += size; - } - if (rawData_.size() * sizeof(std::complex) < totalSize) { + // Validate device memory + size_t totalSize = std::accumulate(componentBufferSizes.begin(), + componentBufferSizes.end(), 0); + if (totalSize > rawData_.size() * sizeof(std::complex)) { throw std::invalid_argument( - "Raw data size is insufficient to cover all components."); + "Device memory size is insufficient to cover all components."); } - // Attach storage for each component + // Attach storage for using device memory (gpuData_) std::vector componentBuffers(numStateComponents); - std::vector *> rawComponentData(numStateComponents); - size_t offset = 0; for (int32_t i = 0; i < numStateComponents; i++) { - rawComponentData[i] = - reinterpret_cast *>(rawData_.data()) + offset; - componentBuffers[i] = static_cast(rawComponentData[i]); + componentBuffers[i] = static_cast(gpuData_ + offset); offset += componentBufferSizes[i] / sizeof(std::complex); } @@ -193,10 +194,9 @@ size_t cudm_state::calculate_density_matrix_size( } // Initialize state based on InitialStateArgT -cudm_state -cudm_state::create_initial_state(const InitialStateArgT &initialStateArg, - const std::vector &hilbertSpaceDims, - bool hasCollapseOps) { +cudm_state cudm_state::create_initial_state( + cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, bool hasCollapseOps) { size_t stateVectorSize = std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), static_cast(1), std::multiplies<>{}); @@ -239,7 +239,7 @@ cudm_state::create_initial_state(const InitialStateArgT &initialStateArg, throw std::invalid_argument("Unsupported InitialStateArgT type."); } - cudm_state state(rawData); + cudm_state state(handle, rawData); state.init_state(hilbertSpaceDims); // Convert to a density matrix if collapse operators are present. diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 86de154442..8a1d01fed4 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -8,50 +8,103 @@ #include "cudaq/cudm_time_stepper.h" #include "cudaq/cudm_error_handling.h" +#include "cudaq/cudm_helpers.h" #include namespace cudaq { -cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) : handle_(handle), liouvillian_(liouvillian) {} +cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian) + : handle_(handle), liouvillian_(liouvillian) {} void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { - if (!state.is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - - std::cout << "Preparing workspace ..." << std::endl; - // Prepare workspace - cudensitymatWorkspaceDescriptor_t workspace; - HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); - if (!workspace) { - throw std::runtime_error("Failed to create workspace for the operator."); - } - - std::cout << "Create a new state for the next step ..." << std::endl; - // Create a new state for the next step - cudm_state next_state(state.to_density_matrix().get_raw_data()); - next_state.init_state(state.get_hilbert_space_dims()); - - if (!next_state.is_initialized()) { - throw std::runtime_error("Next state failed to initialize."); - } - - if (!handle_) { - throw std::runtime_error("cudm_time_stepper handle is not initializes."); - } - - if (!liouvillian_) { - throw std::runtime_error("Liouvillian is not initialized."); - } - - std::cout << "cudensitymatOperatorComputeAction ..." << std::endl; - HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction(handle_, liouvillian_, t, 0, nullptr, state.get_impl(), next_state.get_impl(), workspace, 0)); - - std::cout << "Update the state ..." << std::endl; - // Update the state - state = std::move(next_state); - - std::cout << "Clean up workspace ..." << std::endl; - // Clean up workspace - HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); + if (!state.is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + if (!handle_) { + throw std::runtime_error("cudm_time_stepper handle is not initializes."); + } + + if (!liouvillian_) { + throw std::runtime_error("Liouvillian is not initialized."); + } + + std::cout << "Preparing workspace ..." << std::endl; + // Prepare workspace + cudensitymatWorkspaceDescriptor_t workspace; + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); + + // Query free gpu memory and allocate workspace buffer + std::size_t freeMem = 0, totalMem = 0; + HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); + // Take 80% of free memory + freeMem = static_cast(static_cast(freeMem) * 0.80); + + std::cout << "Max workspace buffer size (bytes): " << freeMem << std::endl; + + std::cout << "Create a new state for the next step ..." << std::endl; + // Create a new state for the next step + cudm_state next_state(handle_, state.get_raw_data()); + next_state.init_state(state.get_hilbert_space_dims()); + + if (!next_state.is_initialized()) { + throw std::runtime_error("Next state failed to initialize."); + } + + if (state.get_hilbert_space_dims() != next_state.get_hilbert_space_dims()) { + throw std::runtime_error( + "As the dimensions of both the old and the new state do no match, the " + "operator cannot act on the states."); + } + + // Prepare the operator for action + std::cout << "Preparing the operator for action ..." << std::endl; + HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( + handle_, liouvillian_, state.get_impl(), next_state.get_impl(), + CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); + + std::cout << "Querying required workspace buffer size ..." << std::endl; + // Query required workspace buffer size + std::size_t requiredBufferSize = 0; + HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( + handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); + + std::cout << "Required workspace buffer size (bytes): " << requiredBufferSize + << std::endl; + + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + auto *workspaceBuffer = create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + std::cout << "Allocated workspace buffer of size (bytes): " + << requiredBufferSize << std::endl; + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + + std::cout << "Attached workspace buffer" << std::endl; + + // Apply the operator action + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( + handle_, liouvillian_, t, 1, std::vector({step_size}).data(), + state.get_impl(), next_state.get_impl(), workspace, 0x0)); + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + + std::cout << "Updated quantum state" << std::endl; + + // Swap states: Move next_state into state + state = std::move(next_state); + + // Cleanup + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); + destroy_array_gpu(workspaceBuffer); + + std::cout << "Cleaned up workspace" << std::endl; } -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index ae455098f5..be7906a30b 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/helpers.h" +#include "cudaq/cudm_error_handling.h" #include #include #include diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index c5e0e264cf..163ecd3f59 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -18,7 +18,11 @@ using namespace cudaq; class CuDensityMatStateTest : public ::testing::Test { protected: + cudensitymatHandle_t handle; + void SetUp() override { + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + // Set up test data for a single 2-qubit system hilbertSpaceDims = {2, 2}; @@ -39,7 +43,7 @@ class CuDensityMatStateTest : public ::testing::Test { std::complex(0.0, 0.0), std::complex(0.0, 0.0)}; } - void TearDown() override {} + void TearDown() override { cudensitymatDestroy(handle); } std::vector hilbertSpaceDims; std::vector> stateVectorData; @@ -47,7 +51,7 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -58,7 +62,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_state state(densityMatrixData); + cudm_state state(handle, densityMatrixData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -73,12 +77,12 @@ TEST_F(CuDensityMatStateTest, InvalidInitialization) { std::vector> invalidData = { std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_state state(invalidData); + cudm_state state(handle, invalidData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); @@ -92,7 +96,7 @@ TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_state state(densityMatrixData); + cudm_state state(handle, densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -100,25 +104,25 @@ TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, DumpUninitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.attach_storage(), std::runtime_error); } TEST_F(CuDensityMatStateTest, DestructorCleansUp) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - cudm_state state(emptyData); + cudm_state state(handle, emptyData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } @@ -127,7 +131,7 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; stateVectorData = {std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); @@ -143,19 +147,19 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); } TEST_F(CuDensityMatStateTest, MultipleInitialization) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_initialized()); @@ -164,7 +168,7 @@ TEST_F(CuDensityMatStateTest, MultipleInitialization) { } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_state state(densityMatrixData); + cudm_state state(handle, densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -172,14 +176,14 @@ TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index d0693fec18..03807d99af 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -6,81 +6,82 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include +#include +#include #include #include -#include -#include +#include using namespace cudaq; // Mock Liouvillian operator creation cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { - cudensitymatOperator_t liouvillian; - std::vector dimensions = {2, 2}; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, static_cast(dimensions.size()), dimensions.data(), &liouvillian)); - return liouvillian; + cudensitymatOperator_t liouvillian; + std::vector dimensions = {2, 2}; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(dimensions.size()), dimensions.data(), + &liouvillian)); + return liouvillian; } // Mock Hilbert space dimensions std::vector> mock_initial_state_data() { - return { - {1.0, 0.0}, {0.0, 0.0}, - {0.0, 0.0}, {0.0, 0.0} - }; + return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; } // Mock initial raw state data -std::vector mock_hilbert_space_dims() { - return {2, 2}; -} +std::vector mock_hilbert_space_dims() { return {2, 2}; } class CuDensityMatTimeStepperTest : public ::testing::Test { protected: - cudensitymatHandle_t handle_; - cudensitymatOperator_t liouvillian_; - cudm_time_stepper *time_stepper_; - cudm_state state_; + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; + cudm_time_stepper *time_stepper_; + cudm_state *state_; + + // CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; - CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; + void SetUp() override { + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); - void SetUp() override { - // Create library handle - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + // Create a mock Liouvillian + liouvillian_ = mock_liouvillian(handle_); - // Create a mock Liouvillian - liouvillian_ = mock_liouvillian(handle_); + // Initialize the time stepper + time_stepper_ = new cudm_time_stepper(handle_, liouvillian_); - // Initialize the time stepper - time_stepper_ = new cudm_time_stepper(liouvillian_, handle_); + state_ = new cudm_state(handle_, mock_initial_state_data()); - // Initialize the state - state_.init_state(mock_hilbert_space_dims()); + // Initialize the state + state_->init_state(mock_hilbert_space_dims()); - ASSERT_TRUE(state_.is_initialized()); - } + ASSERT_TRUE(state_->is_initialized()); + } - void TearDown() override { - // Clean up - HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); - HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); - delete time_stepper_; - } + void TearDown() override { + // Clean up + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + delete time_stepper_; + delete state_; + } }; // Test initialization of cudm_time_stepper -TEST_F(CuDensityMatTimeStepperTest, Initialization) { - ASSERT_NE(time_stepper_, nullptr); - ASSERT_TRUE(state_.is_initialized()); - ASSERT_FALSE(state_.is_density_matrix()); -} +// TEST_F(CuDensityMatTimeStepperTest, Initialization) { +// ASSERT_NE(time_stepper_, nullptr); +// ASSERT_TRUE(state_->is_initialized()); +// ASSERT_FALSE(state_->is_density_matrix()); +// } // Test a single compute step TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { - ASSERT_TRUE(state_.is_initialized()); - EXPECT_NO_THROW(time_stepper_->compute(state_, 0.0, 1.0)); - ASSERT_TRUE(state_.is_initialized()); + ASSERT_TRUE(state_->is_initialized()); + EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); + ASSERT_TRUE(state_->is_initialized()); } - - +// // Add test to use construct_liouvillian and then use compute to step using +// this liouvillian +// // z0 * z1 From 0973be4c07ee8401a037b2cd6bc262f9603f5fd4 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 08:27:16 -0800 Subject: [PATCH 034/311] Exposing handle and adding operator overloading for + and * Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 12 ++++++++++++ runtime/cudaq/dynamics/cudm_state.cpp | 28 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 220136d1f8..0ee57cb67b 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -75,6 +75,18 @@ class cudm_state { /// @return A copy of the hilbert space dimensions of a vector of integers. std::vector get_hilbert_space_dims() const; + /// @brief Returns the handle + /// @return The handle associated with the state + cudensitymatHandle_t get_handle() const; + + /// @brief Addition operator (element-wise) + /// @return The new state after the summation of two states. + cudm_state operator+(const cudm_state &other) const; + + /// @brief Scalar multiplication operator + /// @return The new state after multiplying scalar with the current state. + cudm_state operator*(double scalar) const; + private: std::vector> rawData_; std::complex *gpuData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 6d1174dfd1..b326f5a301 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -88,6 +88,34 @@ std::vector cudm_state::get_hilbert_space_dims() const { return hilbertSpaceDims_; } +cudensitymatHandle_t cudm_state::get_handle() const { return handle_; } + +cudm_state cudm_state::operator+(const cudm_state &other) const { + if (rawData_.size() != other.rawData_.size()) { + throw std::invalid_argument("State size mismatch for addition."); + } + + std::vector> resultData(rawData_.size()); + for (size_t i = 0; i < rawData_.size(); i++) { + resultData[i] = rawData_[i] + other.rawData_[i]; + } + + cudm_state result(handle_, resultData); + result.init_state({static_cast(resultData.size())}); + return result; +} + +cudm_state cudm_state::operator*(double scalar) const { + std::vector> resultData(rawData_.size()); + for (size_t i = 0; i < rawData_.size(); i++) { + resultData[i] = rawData_[i] * scalar; + } + + cudm_state result(handle_, resultData); + result.init_state({static_cast(resultData.size())}); + return result; +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From 0abfabb29aca50f7948fed7a09d55cb3172ea209 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 09:23:49 -0800 Subject: [PATCH 035/311] Removing redundant constructor and updating tests for cudm_state Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 14 ++-- runtime/cudaq/dynamics/cudm_state.cpp | 57 ++++++++--------- unittests/dynamics/test_cudm_state.cpp | 88 +++++--------------------- 3 files changed, 47 insertions(+), 112 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 0ee57cb67b..3b9f6ec05d 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -25,7 +25,8 @@ class cudm_state { public: /// @brief To initialize state with raw data. explicit cudm_state(cudensitymatHandle_t handle, - std::vector> rawData); + const std::vector> rawData, + const std::vector &hilbertSpaceDims); /// @brief Destructor to clean up resources ~cudm_state(); @@ -39,11 +40,6 @@ class cudm_state { cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, const std::vector &hilbertSpaceDims, bool hasCollapseOps); - /// @brief Initialize the state as a density matrix or state vector based on - /// dimensions. - /// @param hilbertSpaceDims Vector representing the Hilbert Space dimensions. - void init_state(const std::vector &hilbertSpaceDims); - /// @brief Check if the state is initialized. /// @return True if the state is initialized, false otherwise. bool is_initialized() const; @@ -64,9 +60,6 @@ class cudm_state { /// @return The underlying state implementation. cudensitymatState_t get_impl() const; - /// @brief Attach raw data to the internal state representation - void attach_storage(); - /// @brief Get a copy of the raw data representing the quantum state. /// @return A copy of the raw data as a vector of complex numbers. std::vector> get_raw_data() const; @@ -94,6 +87,9 @@ class cudm_state { cudensitymatHandle_t handle_; std::vector hilbertSpaceDims_; + /// @brief Attach raw data storage to GPU + void attach_storage(); + /// @brief Calculate the size of the state vector for the given Hilbert space /// dimensions. /// @param hilbertSpaceDims Hilbert space dimensions. diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index b326f5a301..fc881313c2 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -16,8 +16,15 @@ namespace cudaq { cudm_state::cudm_state(cudensitymatHandle_t handle, - std::vector> rawData) - : rawData_(rawData), state_(nullptr), handle_(handle), hilbertSpaceDims_() { + const std::vector> rawData, + const std::vector &hilbertSpaceDims) + : rawData_(rawData), state_(nullptr), handle_(handle), + hilbertSpaceDims_(hilbertSpaceDims) { + + if (rawData_.empty()) { + throw std::invalid_argument("Raw data cannot be empty."); + } + // Allocate device memory size_t dataSize = rawData_.size() * sizeof(std::complex); cudaMalloc(reinterpret_cast(&gpuData_), dataSize); @@ -25,24 +32,8 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, // Copy data from host to device HANDLE_CUDA_ERROR( cudaMemcpy(gpuData_, rawData_.data(), dataSize, cudaMemcpyHostToDevice)); -} - -cudm_state::~cudm_state() { - if (state_) { - cudensitymatDestroyState(state_); - } - if (gpuData_) { - cudaFree(gpuData_); - } -} - -void cudm_state::init_state(const std::vector &hilbertSpaceDims) { - if (state_) { - throw std::runtime_error("State is already initialized."); - } - - hilbertSpaceDims_ = hilbertSpaceDims; + // Determine if this is a denisty matrix or state vector size_t rawDataSize = rawData_.size(); size_t expectedDensityMatrixSize = calculate_density_matrix_size(hilbertSpaceDims); @@ -70,6 +61,15 @@ void cudm_state::init_state(const std::vector &hilbertSpaceDims) { attach_storage(); } +cudm_state::~cudm_state() { + if (state_) { + cudensitymatDestroyState(state_); + } + if (gpuData_) { + cudaFree(gpuData_); + } +} + bool cudm_state::is_initialized() const { return state_ != nullptr; } bool cudm_state::is_density_matrix() const { @@ -100,8 +100,7 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { resultData[i] = rawData_[i] + other.rawData_[i]; } - cudm_state result(handle_, resultData); - result.init_state({static_cast(resultData.size())}); + cudm_state result(handle_, resultData, hilbertSpaceDims_); return result; } @@ -111,8 +110,7 @@ cudm_state cudm_state::operator*(double scalar) const { resultData[i] = rawData_[i] * scalar; } - cudm_state result(handle_, resultData); - result.init_state({static_cast(resultData.size())}); + cudm_state result(handle_, resultData, hilbertSpaceDims_); return result; } @@ -153,8 +151,7 @@ cudm_state cudm_state::to_density_matrix() const { } } - cudm_state densityMatrixState(handle_, densityMatrix); - densityMatrixState.init_state(hilbertSpaceDims_); + cudm_state densityMatrixState(handle_, densityMatrix, hilbertSpaceDims_); return densityMatrixState; } @@ -208,11 +205,8 @@ void cudm_state::attach_storage() { size_t cudm_state::calculate_state_vector_size( const std::vector &hilbertSpaceDims) const { - size_t size = 1; - for (auto dim : hilbertSpaceDims) { - size *= dim; - } - return size; + return std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), 1, + std::multiplies<>()); } size_t cudm_state::calculate_density_matrix_size( @@ -267,8 +261,7 @@ cudm_state cudm_state::create_initial_state( throw std::invalid_argument("Unsupported InitialStateArgT type."); } - cudm_state state(handle, rawData); - state.init_state(hilbertSpaceDims); + cudm_state state(handle, rawData, hilbertSpaceDims); // Convert to a density matrix if collapse operators are present. if (hasCollapseOps && !state.is_density_matrix()) { diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 163ecd3f59..8ac6bffee2 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -51,139 +51,85 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_state state(handle, stateVectorData); - EXPECT_FALSE(state.is_initialized()); + cudm_state state(handle, stateVectorData, hilbertSpaceDims); - EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); EXPECT_TRUE(state.is_initialized()); EXPECT_FALSE(state.is_density_matrix()); - EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_state state(handle, densityMatrixData); - EXPECT_FALSE(state.is_initialized()); + cudm_state state(handle, densityMatrixData, hilbertSpaceDims); - EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); EXPECT_TRUE(state.is_initialized()); EXPECT_TRUE(state.is_density_matrix()); - EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, InvalidInitialization) { // Data size mismatch for hilbertSpaceDims (2x2 system expects size 4 or 16) - std::vector> invalidData = { - std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; + std::vector> invalidData = {{1.0, 0.0}, {0.0, 0.0}}; - cudm_state state(handle, invalidData); - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); + EXPECT_THROW(cudm_state state(handle, invalidData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_state state(handle, stateVectorData); - state.init_state(hilbertSpaceDims); - + cudm_state state(handle, stateVectorData, hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); cudm_state densityMatrixState = state.to_density_matrix(); - EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); - EXPECT_NO_THROW(densityMatrixState.dump()); } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_state state(handle, densityMatrixData); - state.init_state(hilbertSpaceDims); + cudm_state state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); } -TEST_F(CuDensityMatStateTest, DumpUninitializedState) { - cudm_state state(handle, stateVectorData); - EXPECT_THROW(state.dump(), std::runtime_error); -} - -TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.attach_storage(), std::runtime_error); -} - TEST_F(CuDensityMatStateTest, DestructorCleansUp) { - cudm_state state(handle, stateVectorData); - - EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); + EXPECT_NO_THROW( + { cudm_state state(handle, stateVectorData, hilbertSpaceDims); }); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - cudm_state state(handle, emptyData); - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); + EXPECT_THROW(cudm_state state(handle, emptyData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; - stateVectorData = {std::complex(1.0, 0.0), - std::complex(0.0, 0.0)}; - cudm_state state(handle, stateVectorData); - - state.init_state(hilbertSpaceDims); + stateVectorData = {{1.0, 0.0}, {0.0, 0.0}}; + cudm_state state(handle, stateVectorData, hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); - EXPECT_NO_THROW(densityMatrixState.dump()); } TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); -} - -TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.to_density_matrix(), std::runtime_error); -} - -TEST_F(CuDensityMatStateTest, MultipleInitialization) { - cudm_state state(handle, stateVectorData); - state.init_state(hilbertSpaceDims); - - EXPECT_TRUE(state.is_initialized()); - - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::runtime_error); + EXPECT_THROW(cudm_state state(handle, stateVectorData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_state state(handle, densityMatrixData); - state.init_state(hilbertSpaceDims); - + cudm_state state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); EXPECT_TRUE(state.is_initialized()); } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_state state(handle, stateVectorData); - state.init_state(hilbertSpaceDims); - + cudm_state state(handle, stateVectorData, hilbertSpaceDims); EXPECT_NO_THROW(state.dump()); } - -TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.dump(), std::runtime_error); -} From c166ec328db82efeac819ec864b20de749e5112e Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 09:53:08 -0800 Subject: [PATCH 036/311] Implementing compute function in cudm_time_stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/base_time_stepper.h | 7 ++- runtime/cudaq/cudm_time_stepper.h | 2 +- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 57 ++++++++------------ 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index 4488de8b44..2c8150de0f 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -14,6 +14,11 @@ class BaseTimeStepper { public: virtual ~BaseTimeStepper() = default; - virtual void compute(TState &state, double t, double step_size) = 0; + /// @brief Compute the next time step for the given quantum state. + /// @param state The quantum state to evolve. + /// @param t Current time. + /// @param step_size Time step size. + /// @return The updated quantum state after stepping. + virtual TState compute(TState &state, double t, double step_size) = 0; }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/cudaq/cudm_time_stepper.h index a245b42c80..6037ee00f1 100644 --- a/runtime/cudaq/cudm_time_stepper.h +++ b/runtime/cudaq/cudm_time_stepper.h @@ -18,7 +18,7 @@ class cudm_time_stepper : public BaseTimeStepper { explicit cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian); - void compute(cudm_state &state, double t, double step_size) override; + cudm_state compute(cudm_state &state, double t, double step_size); private: cudensitymatHandle_t handle_; diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 8a1d01fed4..90f1277f12 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -16,7 +16,8 @@ cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) : handle_(handle), liouvillian_(liouvillian) {} -void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { +cudm_state cudm_time_stepper::compute(cudm_state &state, double t, + double step_size) { if (!state.is_initialized()) { throw std::runtime_error("State is not initialized."); } @@ -29,7 +30,6 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { throw std::runtime_error("Liouvillian is not initialized."); } - std::cout << "Preparing workspace ..." << std::endl; // Prepare workspace cudensitymatWorkspaceDescriptor_t workspace; HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); @@ -40,13 +40,12 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { // Take 80% of free memory freeMem = static_cast(static_cast(freeMem) * 0.80); - std::cout << "Max workspace buffer size (bytes): " << freeMem << std::endl; - - std::cout << "Create a new state for the next step ..." << std::endl; // Create a new state for the next step - cudm_state next_state(handle_, state.get_raw_data()); - next_state.init_state(state.get_hilbert_space_dims()); - + std::vector> zero_initiailized_data( + state.get_raw_data().size(), {0.0, 0.0}); + cudm_state next_state(handle_, zero_initiailized_data, + state.get_hilbert_space_dims()); + if (!next_state.is_initialized()) { throw std::runtime_error("Next state failed to initialize."); } @@ -58,36 +57,29 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { } // Prepare the operator for action - std::cout << "Preparing the operator for action ..." << std::endl; HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( handle_, liouvillian_, state.get_impl(), next_state.get_impl(), CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); - std::cout << "Querying required workspace buffer size ..." << std::endl; // Query required workspace buffer size std::size_t requiredBufferSize = 0; HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); - std::cout << "Required workspace buffer size (bytes): " << requiredBufferSize - << std::endl; - - // Allocate GPU storage for workspace buffer - const std::size_t bufferVolume = - requiredBufferSize / sizeof(std::complex); - auto *workspaceBuffer = create_array_gpu( - std::vector>(bufferVolume, {0.0, 0.0})); - - std::cout << "Allocated workspace buffer of size (bytes): " - << requiredBufferSize << std::endl; - - // Attach workspace buffer - HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( - handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, - CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); - - std::cout << "Attached workspace buffer" << std::endl; + void *workspaceBuffer = nullptr; + if (requiredBufferSize > 0) { + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + workspaceBuffer = create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + } // Apply the operator action HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); @@ -96,15 +88,10 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { state.get_impl(), next_state.get_impl(), workspace, 0x0)); HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); - std::cout << "Updated quantum state" << std::endl; - - // Swap states: Move next_state into state - state = std::move(next_state); - // Cleanup - HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); destroy_array_gpu(workspaceBuffer); + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); - std::cout << "Cleaned up workspace" << std::endl; + return next_state; } } // namespace cudaq \ No newline at end of file From b470e21e95b08af8a849816d395d05103f580069 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 09:58:26 -0800 Subject: [PATCH 037/311] Adding test_mocks for unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 2 +- unittests/dynamics/test_cudm_time_stepper.cpp | 51 +++++-------------- unittests/dynamics/test_mocks.h | 33 ++++++++++++ 3 files changed, 47 insertions(+), 39 deletions(-) create mode 100644 unittests/dynamics/test_mocks.h diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 90f1277f12..c803b9488b 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -45,7 +45,7 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, state.get_raw_data().size(), {0.0, 0.0}); cudm_state next_state(handle_, zero_initiailized_data, state.get_hilbert_space_dims()); - + if (!next_state.is_initialized()) { throw std::runtime_error("Next state failed to initialize."); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 03807d99af..fbde6b089c 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -6,40 +6,23 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "test_mocks.h" #include #include #include #include #include +#include +#include using namespace cudaq; -// Mock Liouvillian operator creation -cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { - cudensitymatOperator_t liouvillian; - std::vector dimensions = {2, 2}; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(dimensions.size()), dimensions.data(), - &liouvillian)); - return liouvillian; -} - -// Mock Hilbert space dimensions -std::vector> mock_initial_state_data() { - return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; -} - -// Mock initial raw state data -std::vector mock_hilbert_space_dims() { return {2, 2}; } - class CuDensityMatTimeStepperTest : public ::testing::Test { protected: cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; - cudm_time_stepper *time_stepper_; - cudm_state *state_; - - // CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; + std::unique_ptr time_stepper_; + std::unique_ptr state_; void SetUp() override { // Create library handle @@ -49,12 +32,10 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { liouvillian_ = mock_liouvillian(handle_); // Initialize the time stepper - time_stepper_ = new cudm_time_stepper(handle_, liouvillian_); - - state_ = new cudm_state(handle_, mock_initial_state_data()); + time_stepper_ = std::make_unique(handle_, liouvillian_); - // Initialize the state - state_->init_state(mock_hilbert_space_dims()); + state_ = std::make_unique(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()); ASSERT_TRUE(state_->is_initialized()); } @@ -63,17 +44,15 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { // Clean up HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); - delete time_stepper_; - delete state_; } }; // Test initialization of cudm_time_stepper -// TEST_F(CuDensityMatTimeStepperTest, Initialization) { -// ASSERT_NE(time_stepper_, nullptr); -// ASSERT_TRUE(state_->is_initialized()); -// ASSERT_FALSE(state_->is_density_matrix()); -// } +TEST_F(CuDensityMatTimeStepperTest, Initialization) { + ASSERT_NE(time_stepper_, nullptr); + ASSERT_TRUE(state_->is_initialized()); + ASSERT_FALSE(state_->is_density_matrix()); +} // Test a single compute step TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { @@ -81,7 +60,3 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); ASSERT_TRUE(state_->is_initialized()); } - -// // Add test to use construct_liouvillian and then use compute to step using -// this liouvillian -// // z0 * z1 diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h new file mode 100644 index 0000000000..6b81e26d91 --- /dev/null +++ b/unittests/dynamics/test_mocks.h @@ -0,0 +1,33 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +// Mock Liouvillian operator creation +inline cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { + cudensitymatOperator_t liouvillian; + std::vector dimensions = {2, 2}; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(dimensions.size()), dimensions.data(), + &liouvillian)); + return liouvillian; +} + +// Mock Hilbert space dimensions +inline std::vector> mock_initial_state_data() { + return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; +} + +// Mock initial raw state data +inline std::vector mock_hilbert_space_dims() { return {2, 2}; } From 783deacef8d737a52e7b2f0f456af84b242f9876 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 13:36:55 -0800 Subject: [PATCH 038/311] Adding check for step_size=0 condition and few more unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 6 +++- unittests/dynamics/test_cudm_time_stepper.cpp | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index c803b9488b..271689d639 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -18,12 +18,16 @@ cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudm_state cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { + if (step_size == 0.0) { + throw std::runtime_error("Step size cannot be zero."); + } + if (!state.is_initialized()) { throw std::runtime_error("State is not initialized."); } if (!handle_) { - throw std::runtime_error("cudm_time_stepper handle is not initializes."); + throw std::runtime_error("cudm_time_stepper handle is not initialized."); } if (!liouvillian_) { diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index fbde6b089c..1bfe69c79d 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -60,3 +60,34 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); ASSERT_TRUE(state_->is_initialized()); } + +// Compute step when handle is uninitialized +TEST_F(CuDensityMatTimeStepperTest, ComputeStepUninitializedHandle) { + cudm_time_stepper invalidStepper(nullptr, liouvillian_); + EXPECT_THROW(invalidStepper.compute(*state_, 0.0, 1.0), std::runtime_error); +} + +// Compute step when liouvillian is missing +TEST_F(CuDensityMatTimeStepperTest, ComputeStepNoLiouvillian) { + cudm_time_stepper invalidStepper(handle_, nullptr); + EXPECT_THROW(invalidStepper.compute(*state_, 0.0, 1.0), std::runtime_error); +} + +// Compute step with mismatched dimensions +TEST_F(CuDensityMatTimeStepperTest, ComputeStepMistmatchedDimensions) { + EXPECT_THROW(std::unique_ptr mismatchedState = + std::make_unique(handle_, + mock_initial_state_data(), + std::vector{3, 3}), + std::invalid_argument); +} + +// Compute step with zero step size +TEST_F(CuDensityMatTimeStepperTest, ComputeStepZeroStepSize) { + EXPECT_THROW(time_stepper_->compute(*state_, 0.0, 0.0), std::runtime_error); +} + +// Compute step with large time values +TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { + EXPECT_NO_THROW(time_stepper_->compute(*state_, 1e6, 1e3)); +} From 8594a21b0266272c1d1e3ed6fd3b7360af122568 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 13:39:04 -0800 Subject: [PATCH 039/311] Adding #pragma once so that header files are included only once during compilation Signed-off-by: Sachin Pisal --- runtime/cudaq/definition.h | 4 +++- runtime/cudaq/operators.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index bdf5af8ab5..d5013ffc9c 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -1,11 +1,13 @@ /****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * * This source code and the accompanying materials are made available under * * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include "cudaq/qis/state.h" #include "cudaq/utils/tensor.h" diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 5f07f5dd49..6543c5fb72 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -6,6 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include "definition.h" #include "utils/tensor.h" From 997512102756b7f6dfb8ebf2617f3ba6fafbac7a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 14:19:27 -0800 Subject: [PATCH 040/311] * Adding partial implementation of runge-kutta integrator * Removing time_stepper implemented specifically only for runge-kutta integrator as we now have a general cudm_time_stepper * Updating CMakelists.txt accordingly * Removing runge_kutta_test_helpers as we will be using test_mocks instead Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 18 ++- runtime/cudaq/dynamics/CMakeLists.txt | 13 +- .../cudaq/dynamics/runge_kutta_integrator.cpp | 65 ++++++++ runtime/cudaq/runge_kutta_integrator.h | 49 +++--- runtime/cudaq/runge_kutta_time_stepper.h | 33 ---- unittests/CMakeLists.txt | 1 - unittests/dynamics/runge_kutta_test_helpers.h | 24 --- .../dynamics/test_runge_kutta_integrator.cpp | 131 ++++----------- .../test_runge_kutta_time_stepper.cpp | 152 ------------------ 9 files changed, 147 insertions(+), 339 deletions(-) create mode 100644 runtime/cudaq/dynamics/runge_kutta_integrator.cpp delete mode 100644 runtime/cudaq/runge_kutta_time_stepper.h delete mode 100644 unittests/dynamics/runge_kutta_test_helpers.h delete mode 100644 unittests/dynamics/test_runge_kutta_time_stepper.cpp diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index e3196bd52d..0d63acf4d5 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -31,13 +31,27 @@ class BaseIntegrator { virtual void post_init() = 0; public: + /// @brief Default constructor + BaseIntegrator() = default; + + /// @brief Constructor to initialize the integrator with a state and time + /// stepper. + /// @param initial_state Initial quantum state. + /// @param t0 Initial time. + /// @param stepper Time stepper instance. + BaseIntegrator(const TState &initial_state, double t0, + std::shared_ptr> stepper) + : state(initial_state), t(t0), stepper(std::move(stepper)) {} + virtual ~BaseIntegrator() = default; + /// @brief Set the initial state and time void set_state(const TState &initial_state, double t0 = 0.0) { state = initial_state; t = t0; } + /// @brief Set the system parameters (dimensions, schedule, and operators) void set_system( const std::map &dimensions, std::shared_ptr schedule, std::shared_ptr hamiltonian, @@ -48,8 +62,10 @@ class BaseIntegrator { this->collapse_operators = collapse_operators; } - virtual void integrate(double t) = 0; + /// @brief Perform integration to the target time. + virtual void integrate(double target_time) = 0; + /// @brief Get the current time and state. std::pair get_state() const { return {t, state}; } }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 636d8b4096..63129f246f 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,18 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp cudm_time_stepper.cpp + scalar_operators.cpp + elementary_operators.cpp + product_operators.cpp + operator_sum.cpp + schedule.cpp + definition.cpp + helpers.cpp + rydberg_hamiltonian.cpp + cudm_helpers.cpp + cudm_state.cpp + cudm_time_stepper.cpp + runge_kutta_integrator.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp new file mode 100644 index 0000000000..62633bc172 --- /dev/null +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/runge_kutta_integrator.h" +#include + +using namespace cudaq; + +namespace cudaq { +void runge_kutta_integrator::integrate(double target_time) { + if (!stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } + + double dt = integrator_options["dt"]; + if (dt <= 0) { + throw std::invalid_argument("Invalid time step size for integration."); + } + + auto handle = state.get_handle(); + auto hilbertSpaceDims = state.get_hilbert_space_dims(); + + while (t < target_time) { + double step_size = std::min(dt, target_time - 1); + + std::cout << "Runge-Kutta step at time " << t << " with step size: " << step_size << std::endl; + + // Empty vectors of same size as state.get_raw_data() + std::vector> zero_state(state.get_raw_data().size(), {0.0, 0.0}); + + cudm_state k1(handle, zero_state, hilbertSpaceDims); + cudm_state k2(handle, zero_state, hilbertSpaceDims); + cudm_state k3(handle, zero_state, hilbertSpaceDims); + cudm_state k4(handle, zero_state, hilbertSpaceDims); + + if (substeps_ == 1) { + // Euler method (1st order) + k1 = stepper->compute(state, t, step_size); + state = k1; + } else if (substeps_ == 2) { + // Midpoint method (2nd order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size); + state = (k1 + k2) * 0.5; + } else if (substeps_ == 4) { + // Runge-Kutta method (4th order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); + k3 = stepper->compute(k2, t + step_size / 2.0, step_size); + k4 = stepper->compute(k3, t + step_size, step_size); + state = (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); + } + + // Update time + t += step_size; + } + + std::cout << "Integration complete. Final time: " << t << std::endl; +} +} diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 9914258386..0b98bb4d86 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -9,39 +9,38 @@ #pragma once #include "base_integrator.h" -#include "runge_kutta_time_stepper.h" +#include "cudaq/cudm_state.h" +#include "cudaq/cudm_time_stepper.h" #include namespace cudaq { -template -class RungeKuttaIntegrator : public BaseIntegrator { +class runge_kutta_integrator : public BaseIntegrator { public: - using DerivativeFunction = std::function; - - explicit RungeKuttaIntegrator(DerivativeFunction f) - : stepper(std::make_shared>(f)) {} - - // Initializes the integrator - void post_init() override { - if (!this->stepper) { - throw std::runtime_error("Time stepper is not set"); + /// @brief Constructor to initialize the Runge-Kutta integrator + /// @param initial_state Initial quantum state. + /// @param t0 Initial time. + /// @param stepper Time stepper instance. + /// @param substeps Number of Runge-Kutta substeps (must be 1, 2, or 4) + runge_kutta_integrator(const cudm_state &initial_state, double t0, + std::shared_ptr stepper, + int substeps = 4) + : BaseIntegrator(initial_state, t0, stepper), substeps_(substeps) { + if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { + throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); } + post_init(); } - // Advances the system's state from current time to `t` - void integrate(double target_t) override { - if (!this->schedule || !this->hamiltonian) { - throw std::runtime_error("System is not properly set!"); - } + /// @brief Perform Runge-Kutta integration until the target time. + /// @param target_time The final time to integrate to. + void integrate(double t) override; - while (this->t < target_t) { - stepper->compute(this->state, this->t); - // Time step size - this->t += 0.01; - } - } +protected: + /// @brief Any post-initialization setup + void post_init() override {} private: - std::shared_ptr> stepper; + // Number of substeps in RK integration (1, 2, or 4) + int substeps_; }; -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_time_stepper.h b/runtime/cudaq/runge_kutta_time_stepper.h deleted file mode 100644 index 1dcd1f69cc..0000000000 --- a/runtime/cudaq/runge_kutta_time_stepper.h +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "base_time_stepper.h" -#include - -namespace cudaq { -template -class RungeKuttaTimeStepper : public BaseTimeStepper { -public: - using DerivativeFunction = std::function; - - RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} - - void compute(TState &state, double t, double dt = 0.01) override { - // 4th order Runge-Kutta method - TState k1 = derivativeFunc(state, t); - TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); - TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); - TState k4 = derivativeFunc(state + dt * k3, t + dt); - - state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); - } - -private: - DerivativeFunction derivativeFunc; -}; -} // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 74c404b49b..ecc2d68116 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -49,7 +49,6 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/elementary_ops_simple.cpp dynamics/elementary_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp - dynamics/test_runge_kutta_time_stepper.cpp dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp diff --git a/unittests/dynamics/runge_kutta_test_helpers.h b/unittests/dynamics/runge_kutta_test_helpers.h deleted file mode 100644 index 4f93ffa242..0000000000 --- a/unittests/dynamics/runge_kutta_test_helpers.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include - -// A simple state type -using TestState = double; - -// Simple derivative function: dx/dt = -x (exponential decay) -inline TestState simple_derivative(const TestState &state, double t) { - return -state; -} - -// A complex function: dx/dt = sin(t) -inline TestState sine_derivative(const TestState &state, double t) { - return std::sin(t); -} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index c75a7c8d6d..407c211210 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -7,7 +7,7 @@ // ******************************************************************************/ #include "cudaq/runge_kutta_integrator.h" -#include "runge_kutta_test_helpers.h" +#include "test_mocks.h" #include #include #include @@ -17,116 +17,43 @@ using namespace cudaq; // Test fixture class class RungeKuttaIntegratorTest : public ::testing::Test { protected: - RungeKuttaIntegrator *integrator; - std::shared_ptr schedule; - std::shared_ptr hamiltonian; + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; + std::shared_ptr time_stepper_; + std::unique_ptr integrator_; + std::unique_ptr state_; void SetUp() override { - integrator = new RungeKuttaIntegrator(simple_derivative); - // Initial state and time - integrator->set_state(1.0, 0.0); + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); - // A simple step sequence for the schedule - std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; + // Create a mock Liouvillian + liouvillian_ = mock_liouvillian(handle_); - // Dummy parameters - std::vector parameters = {"param1"}; + // Initialize the time stepper + time_stepper_ = std::make_shared(handle_, liouvillian_); - // A simple parameter function - auto value_function = [](const std::string ¶m, - const std::complex &step) { return step; }; + // Create initial state + state_ = std::make_unique(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()); - // A valid schedule instance - schedule = std::make_shared(steps, parameters, value_function); + double t0 = 0.0; + // Initialize the integrator (using substeps = 2, for mid-point rule) + integrator_ = + std::make_unique(*state_, t0, time_stepper_, 2); - // A simple hamiltonian as an operator_sum - hamiltonian = std::make_shared(); - *hamiltonian += 0.5 * elementary_operator::identity(0); - *hamiltonian += 0.5 * elementary_operator::number(0); - - // System with valid components - integrator->set_system({{0, 2}}, schedule, hamiltonian); - } - - void TearDown() override { delete integrator; } -}; - -// Basic integration -TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { - integrator->integrate(1.0); - - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) - << "Basic Runge-Kutta integration failed!"; -} - -// Time evolution -TEST_F(RungeKuttaIntegratorTest, TimeEvolution) { - integrator->integrate(2.0); - - double expected = 2.0; - - EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) - << "Integrator did not correctly update time!"; -} - -// Large step size -TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { - integrator->integrate(5.0); - - double expected = std::exp(-5.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) - << "Runge-Kutta integration failed for large step size!!"; -} - -// // Integrating Sine function -// TEST_F(RungeKuttaIntegratorTest, SineFunction) { -// integrator = new RungeKuttaIntegrator(sine_derivative); -// integrator->set_state(1.0, 0.0); -// integrator->set_system({{0, 2}}, schedule, hamiltonian); - -// integrator->integrate(M_PI / 2); - -// double expected = std::cos(M_PI / 2); - -// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << -// "Runge-Kutta integration for sine function failed!"; -// } - -// Small step size -TEST_F(RungeKuttaIntegratorTest, SmallStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); - - double step_size = 0.001; - while (integrator->get_state().first < 1.0) { - integrator->integrate(integrator->get_state().first + step_size); + ASSERT_TRUE(state_->is_initialized()); } - double expected = std::exp(-1.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) - << "Runge-Kutta integration for small step size failed!"; -} - -// Large step size -TEST_F(RungeKuttaIntegratorTest, LargeStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); - - double step_size = 0.5; - double t = 0.0; - double target_t = 1.0; - while (t < target_t) { - integrator->integrate(std::min(t + step_size, target_t)); - t += step_size; + void TearDown() override { + // Clean up resources + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); } +}; - double expected = std::exp(-1.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) - << "Runge-Kutta integration for large step size failed!"; +// Test Initialization +TEST_F(RungeKuttaIntegratorTest, Initialization) { + ASSERT_NE(integrator_, nullptr); + // ASSERT_TRUE(state_->is_initialized()); } diff --git a/unittests/dynamics/test_runge_kutta_time_stepper.cpp b/unittests/dynamics/test_runge_kutta_time_stepper.cpp deleted file mode 100644 index 4c4c7b7588..0000000000 --- a/unittests/dynamics/test_runge_kutta_time_stepper.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/runge_kutta_time_stepper.h" -#include "runge_kutta_test_helpers.h" -#include -#include -#include - -// Test fixture class -class RungeKuttaTimeStepperTest : public ::testing::Test { -protected: - std::shared_ptr> stepper; - - void SetUp() override { - stepper = std::make_shared>( - simple_derivative); - } -}; - -// Single step integration -TEST_F(RungeKuttaTimeStepperTest, SingleStep) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; - - stepper->compute(state, t, dt); - - // Expected result using analytical solution: x(t) = e^(-t) - double expected = std::exp(-dt); - - EXPECT_NEAR(state, expected, 1e-3) - << "Single step Runge-Kutta integration failed!"; -} - -// Multiple step integration -TEST_F(RungeKuttaTimeStepperTest, MultipleSteps) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; - - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } - - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-2) - << "Multiple step Runge-Kutta integration failed!"; -} - -// Convergence to Analytical Solution -TEST_F(RungeKuttaTimeStepperTest, Convergence) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.01; - int steps = 100; - - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } - - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-3) - << "Runge-Kutta integration does not converge!"; -} - -// // Integrating Sine function -// TEST_F(RungeKuttaTimeStepperTest, SineFunction) { -// auto sine_stepper = -// std::make_shared>(sine_derivative); - -// // Initial values -// double state = 0.0; -// double t = 0.0; -// double dt = 0.1; -// int steps = 10; - -// for (int i = 0; i < steps; i++) { -// sine_stepper->compute(state, t, dt); -// } - -// // Expected integral of sin(t) over [0, 1] is 1 - cos(1) -// double expected = 1 - std::cos(1); - -// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine -// function failed!"; -// } - -// Handling small steps sizes -TEST_F(RungeKuttaTimeStepperTest, SmallStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1e-5; - int steps = 100000; - - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } - - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-3) - << "Runge-Kutta fails with small step sizes!"; -} - -// Handling large steps sizes -TEST_F(RungeKuttaTimeStepperTest, LargeStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1.0; - - stepper->compute(state, t, dt); - - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-1) - << "Runge-Kutta is unstable with large step sizes!"; -} - -// Constant derivative (dx/dt = 0) -TEST_F(RungeKuttaTimeStepperTest, ConstantFunction) { - auto constant_stepper = - std::make_shared>( - [](const TestState &state, double t) { return 0.0; }); - - // Initial values - double state = 5.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; - - for (int i = 0; i < steps; i++) { - constant_stepper->compute(state, t, dt); - } - - EXPECT_NEAR(state, 5.0, 1e-6) - << "Runge-Kutta should not change a constant function!"; -} From 4315b5239911698e0f94ca0b09e0ed508f5a5f98 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 20:28:04 -0800 Subject: [PATCH 041/311] * Implementing move constructor and move assignment * Deleting the copy constructor to prevent unintended copies * Implementing Runge-Kutta integrator using Euler, Midpoint, and Runge-Kutta 4th order methods * Adding unittests for Runge-Kutta integrator Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 15 ++- runtime/cudaq/cudm_state.h | 8 ++ runtime/cudaq/dynamics/cudm_state.cpp | 45 +++++++- .../cudaq/dynamics/runge_kutta_integrator.cpp | 85 +++++++------- runtime/cudaq/runge_kutta_integrator.h | 14 ++- unittests/dynamics/test_mocks.h | 26 ++++- .../dynamics/test_runge_kutta_integrator.cpp | 104 ++++++++++++++++-- 7 files changed, 235 insertions(+), 62 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 0d63acf4d5..31d82ea201 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -39,9 +39,13 @@ class BaseIntegrator { /// @param initial_state Initial quantum state. /// @param t0 Initial time. /// @param stepper Time stepper instance. - BaseIntegrator(const TState &initial_state, double t0, + BaseIntegrator(TState &&initial_state, double t0, std::shared_ptr> stepper) - : state(initial_state), t(t0), stepper(std::move(stepper)) {} + : state(std::move(initial_state)), t(t0), stepper(std::move(stepper)) { + if (!this->stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } + } virtual ~BaseIntegrator() = default; @@ -51,6 +55,11 @@ class BaseIntegrator { t = t0; } + /// @brief Set an option for the integrator + void set_option(const std::string &key, double value) { + integrator_options[key] = value; + } + /// @brief Set the system parameters (dimensions, schedule, and operators) void set_system( const std::map &dimensions, std::shared_ptr schedule, @@ -66,6 +75,6 @@ class BaseIntegrator { virtual void integrate(double target_time) = 0; /// @brief Get the current time and state. - std::pair get_state() const { return {t, state}; } + std::pair get_state() const { return {t, state}; } }; } // namespace cudaq diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 3b9f6ec05d..ec7cce4f8a 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -28,6 +28,14 @@ class cudm_state { const std::vector> rawData, const std::vector &hilbertSpaceDims); + // Prevent copies (avoids double free issues) + cudm_state(const cudm_state &) = delete; + cudm_state &operator=(const cudm_state &) = delete; + + // Allow move semantics + cudm_state(cudm_state &&other) noexcept; + cudm_state &operator=(cudm_state &&other) noexcept; + /// @brief Destructor to clean up resources ~cudm_state(); diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index fc881313c2..170a93d483 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace cudaq { @@ -61,12 +62,47 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, attach_storage(); } +cudm_state::cudm_state(cudm_state &&other) noexcept + : rawData_(std::move(other.rawData_)), gpuData_(other.gpuData_), + state_(other.state_), handle_(other.handle_), + hilbertSpaceDims_(std::move(other.hilbertSpaceDims_)) { + other.gpuData_ = nullptr; + other.state_ = nullptr; +} + +cudm_state &cudm_state::operator=(cudm_state &&other) noexcept { + if (this != &other) { + // Free existing resources + if (state_) { + cudensitymatDestroyState(state_); + } + if (gpuData_) { + cudaFree(gpuData_); + } + + // Move data from other + rawData_ = std::move(other.rawData_); + gpuData_ = other.gpuData_; + state_ = other.state_; + handle_ = other.handle_; + hilbertSpaceDims_ = std::move(other.hilbertSpaceDims_); + + // Nullify other + other.gpuData_ = nullptr; + other.state_ = nullptr; + } + + return *this; +} + cudm_state::~cudm_state() { if (state_) { cudensitymatDestroyState(state_); + state_ = nullptr; } if (gpuData_) { cudaFree(gpuData_); + gpuData_ = nullptr; } } @@ -100,8 +136,7 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { resultData[i] = rawData_[i] + other.rawData_[i]; } - cudm_state result(handle_, resultData, hilbertSpaceDims_); - return result; + return cudm_state(handle_, resultData, hilbertSpaceDims_); } cudm_state cudm_state::operator*(double scalar) const { @@ -110,8 +145,7 @@ cudm_state cudm_state::operator*(double scalar) const { resultData[i] = rawData_[i] * scalar; } - cudm_state result(handle_, resultData, hilbertSpaceDims_); - return result; + return cudm_state(handle_, resultData, hilbertSpaceDims_); } std::string cudm_state::dump() const { @@ -151,8 +185,7 @@ cudm_state cudm_state::to_density_matrix() const { } } - cudm_state densityMatrixState(handle_, densityMatrix, hilbertSpaceDims_); - return densityMatrixState; + return cudm_state(handle_, densityMatrix, hilbertSpaceDims_); } cudensitymatState_t cudm_state::get_impl() const { diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 62633bc172..abf0291419 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -13,53 +13,60 @@ using namespace cudaq; namespace cudaq { void runge_kutta_integrator::integrate(double target_time) { - if (!stepper) { - throw std::runtime_error("Time stepper is not initialized."); - } + if (!stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } - double dt = integrator_options["dt"]; - if (dt <= 0) { - throw std::invalid_argument("Invalid time step size for integration."); - } + if (integrator_options.find("dt") == integrator_options.end()) { + throw std::invalid_argument( + "Time step size (dt) is missing from integrator options."); + } - auto handle = state.get_handle(); - auto hilbertSpaceDims = state.get_hilbert_space_dims(); + double dt = integrator_options["dt"]; + if (dt <= 0) { + throw std::invalid_argument("Invalid time step size for integration."); + } - while (t < target_time) { - double step_size = std::min(dt, target_time - 1); + auto handle = state.get_handle(); + auto hilbertSpaceDims = state.get_hilbert_space_dims(); - std::cout << "Runge-Kutta step at time " << t << " with step size: " << step_size << std::endl; + while (t < target_time) { + double step_size = std::min(dt, target_time - t); - // Empty vectors of same size as state.get_raw_data() - std::vector> zero_state(state.get_raw_data().size(), {0.0, 0.0}); + std::cout << "Runge-Kutta step at time " << t + << " with step size: " << step_size << std::endl; - cudm_state k1(handle, zero_state, hilbertSpaceDims); - cudm_state k2(handle, zero_state, hilbertSpaceDims); - cudm_state k3(handle, zero_state, hilbertSpaceDims); - cudm_state k4(handle, zero_state, hilbertSpaceDims); + // Empty vectors of same size as state.get_raw_data() + std::vector> zero_state(state.get_raw_data().size(), + {0.0, 0.0}); - if (substeps_ == 1) { - // Euler method (1st order) - k1 = stepper->compute(state, t, step_size); - state = k1; - } else if (substeps_ == 2) { - // Midpoint method (2nd order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size); - state = (k1 + k2) * 0.5; - } else if (substeps_ == 4) { - // Runge-Kutta method (4th order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); - k3 = stepper->compute(k2, t + step_size / 2.0, step_size); - k4 = stepper->compute(k3, t + step_size, step_size); - state = (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); - } + cudm_state k1(handle, zero_state, hilbertSpaceDims); + cudm_state k2(handle, zero_state, hilbertSpaceDims); + cudm_state k3(handle, zero_state, hilbertSpaceDims); + cudm_state k4(handle, zero_state, hilbertSpaceDims); - // Update time - t += step_size; + if (substeps_ == 1) { + // Euler method (1st order) + k1 = stepper->compute(state, t, step_size); + state = std::move(k1); + } else if (substeps_ == 2) { + // Midpoint method (2nd order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size); + state = std::move((k1 + k2) * 0.5); + } else if (substeps_ == 4) { + // Runge-Kutta method (4th order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); + k3 = stepper->compute(k2, t + step_size / 2.0, step_size); + k4 = stepper->compute(k3, t + step_size, step_size); + state = std::move((k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0)); } - std::cout << "Integration complete. Final time: " << t << std::endl; -} + // Update time + t += step_size; + } + + std::cout << "Integration complete. Final time: " << t << std::endl; } +} // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 0b98bb4d86..fa9585164f 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -8,9 +8,10 @@ #pragma once -#include "base_integrator.h" +#include "cudaq/base_integrator.h" #include "cudaq/cudm_state.h" #include "cudaq/cudm_time_stepper.h" +#include #include namespace cudaq { @@ -21,10 +22,15 @@ class runge_kutta_integrator : public BaseIntegrator { /// @param t0 Initial time. /// @param stepper Time stepper instance. /// @param substeps Number of Runge-Kutta substeps (must be 1, 2, or 4) - runge_kutta_integrator(const cudm_state &initial_state, double t0, + runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4) - : BaseIntegrator(initial_state, t0, stepper), substeps_(substeps) { + : BaseIntegrator(std::move(initial_state), t0, stepper), + substeps_(substeps) { + if (!stepper) { + throw std::invalid_argument("Time stepper must be initialized."); + } + if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); } @@ -33,7 +39,7 @@ class runge_kutta_integrator : public BaseIntegrator { /// @brief Perform Runge-Kutta integration until the target time. /// @param target_time The final time to integrate to. - void integrate(double t) override; + void integrate(double target_time) override; protected: /// @brief Any post-initialization setup diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 6b81e26d91..7715b14a7d 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -16,18 +16,38 @@ // Mock Liouvillian operator creation inline cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { - cudensitymatOperator_t liouvillian; + cudensitymatOperator_t liouvillian = nullptr; std::vector dimensions = {2, 2}; HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle, static_cast(dimensions.size()), dimensions.data(), &liouvillian)); + + if (!liouvillian) { + throw std::runtime_error("Failed to create mock Liouvillian!"); + } + return liouvillian; } // Mock Hilbert space dimensions inline std::vector> mock_initial_state_data() { - return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + std::vector> data = { + {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + + if (data.size() != 4) { + throw std::runtime_error("Mock initial state data has incorrect size!"); + } + + return data; } // Mock initial raw state data -inline std::vector mock_hilbert_space_dims() { return {2, 2}; } +inline std::vector mock_hilbert_space_dims() { + std::vector dims = {2, 2}; + + if (dims.empty()) { + throw std::runtime_error("Mock Hilbert space dimensions are empty!"); + } + + return dims; +} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 407c211210..0200d01d8e 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -14,7 +14,6 @@ using namespace cudaq; -// Test fixture class class RungeKuttaIntegratorTest : public ::testing::Test { protected: cudensitymatHandle_t handle_; @@ -32,17 +31,19 @@ class RungeKuttaIntegratorTest : public ::testing::Test { // Initialize the time stepper time_stepper_ = std::make_shared(handle_, liouvillian_); + ASSERT_NE(time_stepper_, nullptr); // Create initial state state_ = std::make_unique(handle_, mock_initial_state_data(), mock_hilbert_space_dims()); + ASSERT_NE(state_, nullptr); + ASSERT_TRUE(state_->is_initialized()); double t0 = 0.0; - // Initialize the integrator (using substeps = 2, for mid-point rule) - integrator_ = - std::make_unique(*state_, t0, time_stepper_, 2); - - ASSERT_TRUE(state_->is_initialized()); + // Initialize the integrator (using substeps = 4, for Runge-Kutta method) + ASSERT_NO_THROW(integrator_ = std::make_unique( + std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NE(integrator_, nullptr); } void TearDown() override { @@ -55,5 +56,94 @@ class RungeKuttaIntegratorTest : public ::testing::Test { // Test Initialization TEST_F(RungeKuttaIntegratorTest, Initialization) { ASSERT_NE(integrator_, nullptr); - // ASSERT_TRUE(state_->is_initialized()); +} + +// Integration with Euler Method (substeps = 1) +TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { + auto eulerIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 1); + eulerIntegrator->set_option("dt", 0.1); + EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); +} + +// Integration with Midpoint Rule (substeps = 2) +TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { + auto midpointIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); + integrator_->set_option("dt", 0.1); + EXPECT_NO_THROW(integrator_->integrate(1.0)); +} + +// Integration with Runge-Kutta 4 (substeps = 4, which is the default value) +TEST_F(RungeKuttaIntegratorTest, RungeKutta4Integration) { + integrator_->set_option("dt", 0.1); + EXPECT_NO_THROW(integrator_->integrate(1.0)); +} + +// Basic Integration Test +TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { + auto [t_before, state_before] = integrator_->get_state(); + integrator_->set_option("dt", 0.1); + + EXPECT_NO_THROW(integrator_->integrate(1.0)); + + auto [t_after, state_after] = integrator_->get_state(); + EXPECT_GT(t_after, t_before); +} + +// Multiple Integration Steps +TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { + integrator_->set_option("dt", 0.1); + integrator_->integrate(0.5); + auto [t_mid, _] = integrator_->get_state(); + + EXPECT_EQ(t_mid, 0.5); + + integrator_->integrate(1.0); + auto [t_final, __] = integrator_->get_state(); + + EXPECT_EQ(t_final, 1.0); +} + +// Missing Time Step (dt) +TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { + auto integrator_missing_dt = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); + + EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); +} + +// Invalid Time Step (dt <= 0) +TEST_F(RungeKuttaIntegratorTest, InvalidTimeStepSize) { + integrator_->set_option("dt", -0.1); + EXPECT_THROW(integrator_->integrate(1.0), std::invalid_argument); +} + +// Zero Integration Time +TEST_F(RungeKuttaIntegratorTest, ZeroIntegrationTime) { + auto [t_before, state_before] = integrator_->get_state(); + integrator_->set_option("dt", 0.1); + + EXPECT_NO_THROW(integrator_->integrate(0.0)); + + auto [t_after, state_after] = integrator_->get_state(); + EXPECT_EQ(t_before, t_after); +} + +// Large Time Step +TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { + integrator_->set_option("dt", 100); + EXPECT_NO_THROW(integrator_->integrate(0.0)); +} + +// Invalid Substeps +TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { + EXPECT_THROW(std::make_unique( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 3), + std::invalid_argument); } From 1355a4d08652acb1f90627c5ffcf402cf490e3d0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sun, 2 Feb 2025 09:12:33 -0800 Subject: [PATCH 042/311] temp Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 6 -- runtime/cudaq/dynamics/cudm_helpers.cpp | 87 +++++++++++------------- runtime/cudaq/dynamics/cudm_solver.cpp | 4 +- unittests/dynamics/test_cudm_helpers.cpp | 44 ++++++------ 4 files changed, 66 insertions(+), 75 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 08e7a6c7d3..6ed2fc087b 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -17,15 +17,9 @@ #include namespace cudaq { -cudensitymatState_t initialize_state(cudensitymatHandle_t handle, - cudensitymatStatePurity_t purity, - const std::vector &mode_extents); - void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream); -void destroy_state(cudensitymatState_t state); - cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 6325189452..8dc21d1cfd 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -43,7 +43,15 @@ get_subspace_extents(const std::vector &mode_extents, cudensitymatElementaryOperator_t create_elementary_operator( cudensitymatHandle_t handle, const std::vector &subspace_extents, const std::vector> &flat_matrix) { - cudensitymatElementaryOperator_t cudm_elem_op; + if (flat_matrix.empty()) { + throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); + } + + if (subspace_extents.empty()) { + throw std::invalid_argument("subspace_extents cannot be empty."); + } + + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; std::vector interleaved_matrix; interleaved_matrix.reserve(flat_matrix.size() * 2); @@ -53,11 +61,16 @@ cudensitymatElementaryOperator_t create_elementary_operator( interleaved_matrix.push_back(value.imag()); } - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + cudensitymatStatus_t status = cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, static_cast(interleaved_matrix.data()), - {nullptr, nullptr}, &cudm_elem_op)); + {nullptr, nullptr}, &cudm_elem_op); + + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + std::cerr << "Error: Failed to create elementary operator. Status: " << status << std::endl; + return nullptr; + } return cudm_elem_op; } @@ -65,12 +78,18 @@ cudensitymatElementaryOperator_t create_elementary_operator( // Function to append an elementary operator to a term void append_elementary_operator_to_term( cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, - cudensitymatElementaryOperator_t &elem_op, + const cudensitymatElementaryOperator_t &elem_op, const std::vector °rees) { + if (degrees.empty()) { + throw std::invalid_argument("Degrees vector cannot be empty."); + } + + std::vector elem_ops = {elem_op}; + std::vector modeActionDuality(degrees.size(), 0); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, static_cast(degrees.size()), &elem_op, + handle, term, static_cast(degrees.size()), elem_ops.data(), degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); } @@ -86,19 +105,6 @@ void append_scalar_to_term(cudensitymatHandle_t handle, {make_cuDoubleComplex(coeff.real(), coeff.imag())}, {nullptr, nullptr})); } -cudensitymatState_t initialize_state(cudensitymatHandle_t handle, - cudensitymatStatePurity_t purity, - const std::vector &mode_extents) { - cudensitymatState_t state; - cudensitymatStatus_t status = - cudensitymatCreateState(handle, purity, mode_extents.size(), - mode_extents.data(), 1, CUDA_C_64F, &state); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - std::cerr << "Error in cudensitymatCreateState: " << status << std::endl; - } - return state; -} - void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { if (!state) { @@ -109,10 +115,6 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); } -void destroy_state(cudensitymatState_t state) { - cudensitymatDestroyState(state); -} - cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, @@ -140,14 +142,13 @@ compute_lindblad_operator(cudensitymatHandle_t handle, handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); - // Create elementary operator form c_op - auto cudm_elem_op = + // Create elementary operator from c_op + cudensitymatElementaryOperator_t cudm_elem_op = create_elementary_operator(handle, mode_extents, flat_matrix); - // Add the elementary operator to the term - // TODO: Fix temp vector below - std::vector temp; - append_elementary_operator_to_term(handle, term, cudm_elem_op, temp); + // Append the elementary operator to the term + std::vector degrees = {0, 1}; + append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; @@ -175,6 +176,10 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + try { cudensitymatOperator_t operator_handle; HANDLE_CUDM_ERROR(cudensitymatCreateOperator( @@ -233,38 +238,28 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( } } -cudensitymatOperator_t construct_liovillian( +cudensitymatOperator_t construct_liouvillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { try { cudensitymatOperator_t liouvillian; - auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create Liouvillian operator."); - } + HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, - {1.0, 0.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add hamiltonian term."); - } + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, + {1.0, 0.0}, scalarCallback)); + // Collapse operator scaled by gamma cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); for (const auto &c_op : collapse_operators) { - status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, - coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add collapse operator term."); - } + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, + coefficient, scalarCallback)); } return liouvillian; } catch (const std::exception &e) { - std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; + std::cerr << "Error in construct_liouvillian: " << e.what() << std::endl; throw; } } diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/cudaq/dynamics/cudm_solver.cpp index dc35b7ce97..38f9a23591 100644 --- a/runtime/cudaq/dynamics/cudm_solver.cpp +++ b/runtime/cudaq/dynamics/cudm_solver.cpp @@ -7,9 +7,9 @@ ******************************************************************************/ #include "cudaq/cudm_solver.h" -#include "cudaq/base_time_stepper.h" #include "cudaq/cudm_helpers.h" #include "cudaq/cudm_state.h" +#include "cudaq/cudm_time_stepper.h" namespace cudaq { cudm_solver::cudm_solver(const Config &config) : config_(config) { @@ -55,6 +55,6 @@ void cudm_solver::evolve( auto handle = state.get_impl(); // Initialize the stepper - BaseTimeStepper timeStepper(liouvillian, handle); + cudm_time_stepper time_stepper(handle, liouvillian); } } // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 734470365a..2512d047ef 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -8,6 +8,7 @@ #include #include +#include #include // Initialize operator_sum @@ -37,37 +38,36 @@ class CuDensityMatTestFixture : public ::testing::Test { void SetUp() override { HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); - HANDLE_CUDA_ERROR(cudaStreamCreate(&stream)); + stream = 0; } void TearDown() override { - HANDLE_CUDA_ERROR(cudaStreamDestroy(stream)); - HANDLE_CUDM_ERROR(cudensitymatDestroy(handle)); + cudensitymatDestroy(handle); } }; // Test for initialize_state TEST_F(CuDensityMatTestFixture, InitializeState) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2}; + + std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - ASSERT_NE(state, nullptr); + cudaq::cudm_state state(handle, rawData, mode_extents); - cudaq::destroy_state(state); + ASSERT_TRUE(state.is_initialized()); } // Test for scale_state TEST_F(CuDensityMatTestFixture, ScaleState) { std::vector mode_extents = {2}; - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - ASSERT_NE(state, nullptr); + std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + cudaq::cudm_state state(handle, rawData, mode_extents); - cudaq::destroy_state(state); + ASSERT_TRUE(state.is_initialized()); + + EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), 2.0, stream)); } // Test for compute_lindblad_op @@ -78,11 +78,12 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); std::vector c_ops = {c_op1, c_op2}; - auto lindblad_op = + EXPECT_NO_THROW({ + auto lindblad_op = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - ASSERT_NE(lindblad_op, nullptr); - - cudensitymatDestroyOperator(lindblad_op); + ASSERT_NE(lindblad_op, nullptr); + cudensitymatDestroyOperator(lindblad_op); + }); } // Test for convert_to_cudensitymat_operator @@ -91,11 +92,12 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { auto op_sum = initialize_operator_sum(); - auto result = + EXPECT_NO_THROW({ + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); - - cudensitymatDestroyOperator(result); + ASSERT_NE(result, nullptr); + cudensitymatDestroyOperator(result); + }); } // Test invalid handle From 154d05d78b270f78131ca8b52d69b93673fc1b62 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 3 Feb 2025 14:47:14 -0800 Subject: [PATCH 043/311] Using cudensitymatStateComputeAccumulation and cudensitymatStateComputeScaling for addition and multiplication of cudm_states Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 14 +++++----- runtime/cudaq/dynamics/cudm_state.cpp | 34 +++++++++++++++++------- unittests/dynamics/test_cudm_helpers.cpp | 10 +++---- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 8dc21d1cfd..a04869d28c 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -68,7 +68,8 @@ cudensitymatElementaryOperator_t create_elementary_operator( {nullptr, nullptr}, &cudm_elem_op); if (status != CUDENSITYMAT_STATUS_SUCCESS) { - std::cerr << "Error: Failed to create elementary operator. Status: " << status << std::endl; + std::cerr << "Error: Failed to create elementary operator. Status: " + << status << std::endl; return nullptr; } @@ -244,17 +245,18 @@ cudensitymatOperator_t construct_liouvillian( double gamma) { try { cudensitymatOperator_t liouvillian; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); + HANDLE_CUDM_ERROR( + cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, - {1.0, 0.0}, scalarCallback)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback)); // Collapse operator scaled by gamma cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); for (const auto &c_op : collapse_operators) { - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, - coefficient, scalarCallback)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, c_op, 0, coefficient, scalarCallback)); } return liouvillian; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 170a93d483..bac537db39 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -131,21 +131,35 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { throw std::invalid_argument("State size mismatch for addition."); } - std::vector> resultData(rawData_.size()); - for (size_t i = 0; i < rawData_.size(); i++) { - resultData[i] = rawData_[i] + other.rawData_[i]; - } + cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + + double scalingFactor = 1.0; + double *gpuScalingFactor; + cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); + cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), + cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( + handle_, other.get_impl(), result.get_impl(), gpuScalingFactor, 0)); + + cudaFree(gpuScalingFactor); - return cudm_state(handle_, resultData, hilbertSpaceDims_); + return result; } cudm_state cudm_state::operator*(double scalar) const { - std::vector> resultData(rawData_.size()); - for (size_t i = 0; i < rawData_.size(); i++) { - resultData[i] = rawData_[i] * scalar; - } + cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + + double *gpuScalar; + cudaMalloc(reinterpret_cast(&gpuScalar), sizeof(double)); + cudaMemcpy(gpuScalar, &scalar, sizeof(double), cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeScaling(handle_, result.get_impl(), + gpuScalar, 0)); + + cudaFree(gpuScalar); - return cudm_state(handle_, resultData, hilbertSpaceDims_); + return result; } std::string cudm_state::dump() const { diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 2512d047ef..e747cc97db 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -41,9 +41,7 @@ class CuDensityMatTestFixture : public ::testing::Test { stream = 0; } - void TearDown() override { - cudensitymatDestroy(handle); - } + void TearDown() override { cudensitymatDestroy(handle); } }; // Test for initialize_state @@ -80,7 +78,7 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { EXPECT_NO_THROW({ auto lindblad_op = - cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); ASSERT_NE(lindblad_op, nullptr); cudensitymatDestroyOperator(lindblad_op); }); @@ -93,8 +91,8 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = - cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, + mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); From 3ab4e769576f64820756ac1783ed71c08c94fe4a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 3 Feb 2025 14:54:10 -0800 Subject: [PATCH 044/311] Allocating k1, k2, k3, k4 inside the scope blocks Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index abf0291419..cde9d010a2 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -27,39 +27,28 @@ void runge_kutta_integrator::integrate(double target_time) { throw std::invalid_argument("Invalid time step size for integration."); } - auto handle = state.get_handle(); - auto hilbertSpaceDims = state.get_hilbert_space_dims(); - while (t < target_time) { double step_size = std::min(dt, target_time - t); std::cout << "Runge-Kutta step at time " << t << " with step size: " << step_size << std::endl; - // Empty vectors of same size as state.get_raw_data() - std::vector> zero_state(state.get_raw_data().size(), - {0.0, 0.0}); - - cudm_state k1(handle, zero_state, hilbertSpaceDims); - cudm_state k2(handle, zero_state, hilbertSpaceDims); - cudm_state k3(handle, zero_state, hilbertSpaceDims); - cudm_state k4(handle, zero_state, hilbertSpaceDims); - if (substeps_ == 1) { // Euler method (1st order) - k1 = stepper->compute(state, t, step_size); + cudm_state k1 = stepper->compute(state, t, step_size); state = std::move(k1); } else if (substeps_ == 2) { // Midpoint method (2nd order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size); + cudm_state k1 = stepper->compute(state, t, step_size / 2.0); + cudm_state k2 = stepper->compute(k1, t + step_size / 2.0, step_size); state = std::move((k1 + k2) * 0.5); } else if (substeps_ == 4) { // Runge-Kutta method (4th order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); - k3 = stepper->compute(k2, t + step_size / 2.0, step_size); - k4 = stepper->compute(k3, t + step_size, step_size); + cudm_state k1 = stepper->compute(state, t, step_size / 2.0); + cudm_state k2 = + stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); + cudm_state k3 = stepper->compute(k2, t + step_size / 2.0, step_size); + cudm_state k4 = stepper->compute(k3, t + step_size, step_size); state = std::move((k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0)); } From d7d2aaead1fc24b43ddb657b57f0f265df6bfb6d Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 3 Feb 2025 15:02:25 -0800 Subject: [PATCH 045/311] Adding operator+= to do an accumulation instead of moving the state Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 4 ++++ runtime/cudaq/dynamics/cudm_state.cpp | 19 +++++++++++++++++++ .../cudaq/dynamics/runge_kutta_integrator.cpp | 6 +++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index ec7cce4f8a..486e3034ac 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -84,6 +84,10 @@ class cudm_state { /// @return The new state after the summation of two states. cudm_state operator+(const cudm_state &other) const; + /// @brief Accumulation operator + /// @return Accumulates the summation of two states. + cudm_state &operator+=(const cudm_state &other); + /// @brief Scalar multiplication operator /// @return The new state after multiplying scalar with the current state. cudm_state operator*(double scalar) const; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index bac537db39..c44bde5d2a 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -147,6 +147,25 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { return result; } +cudm_state &cudm_state::operator+=(const cudm_state &other) { + if (rawData_.size() != other.rawData_.size()) { + throw std::invalid_argument("State size mismatch for addition."); + } + + double scalingFactor = 1.0; + double *gpuScalingFactor; + cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); + cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), + cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( + handle_, other.get_impl(), state_, gpuScalingFactor, 0)); + + cudaFree(gpuScalingFactor); + + return *this; +} + cudm_state cudm_state::operator*(double scalar) const { cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index cde9d010a2..e966ef6ec8 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -36,12 +36,12 @@ void runge_kutta_integrator::integrate(double target_time) { if (substeps_ == 1) { // Euler method (1st order) cudm_state k1 = stepper->compute(state, t, step_size); - state = std::move(k1); + state += k1; } else if (substeps_ == 2) { // Midpoint method (2nd order) cudm_state k1 = stepper->compute(state, t, step_size / 2.0); cudm_state k2 = stepper->compute(k1, t + step_size / 2.0, step_size); - state = std::move((k1 + k2) * 0.5); + state += (k1 + k2) * 0.5; } else if (substeps_ == 4) { // Runge-Kutta method (4th order) cudm_state k1 = stepper->compute(state, t, step_size / 2.0); @@ -49,7 +49,7 @@ void runge_kutta_integrator::integrate(double target_time) { stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); cudm_state k3 = stepper->compute(k2, t + step_size / 2.0, step_size); cudm_state k4 = stepper->compute(k3, t + step_size, step_size); - state = std::move((k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0)); + state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } // Update time From 0d243b85778df7751901669c4b66d25f91e33a26 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Jan 2025 15:19:01 +0000 Subject: [PATCH 046/311] just some test renaming to make it easier to find Signed-off-by: Bettina Heim --- unittests/dynamics/elementary_ops_arithmetic.cpp | 6 +++--- unittests/dynamics/elementary_ops_simple.cpp | 4 ++-- unittests/dynamics/operator_sum.cpp | 12 ++++++------ unittests/dynamics/product_operators_arithmetic.cpp | 12 ++++++------ unittests/dynamics/scalar_ops_arithmetic.cpp | 4 ++-- unittests/dynamics/scalar_ops_simple.cpp | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index fb5c85ac49..0127e0bc39 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -105,7 +105,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { } // namespace utils_0 -TEST(ExpressionTester, checkPreBuiltElementaryOpsScalars) { +TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto function = [](std::map> parameters) { return parameters["value"]; @@ -284,7 +284,7 @@ TEST(ExpressionTester, checkPreBuiltElementaryOpsScalars) { } /// Prebuilt elementary ops against one another. -TEST(ExpressionTester, checkPreBuiltElementaryOpsSelf) { +TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { /// Keeping this fixed throughout. int level_count = 3; @@ -412,7 +412,7 @@ TEST(ExpressionTester, checkPreBuiltElementaryOpsSelf) { /// Testing arithmetic between elementary operators and operator /// sums. -TEST(ExpressionTester, checkElementaryOpsAgainstOpSum) { +TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// Keeping this fixed throughout. int level_count = 3; diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp index 172beeffe8..b45d3d8952 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -99,7 +99,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { } // namespace utils -TEST(ExpressionTester, checkPreBuiltElementaryOps) { +TEST(OperatorExpressions, checkPreBuiltElementaryOps) { std::vector levels = {2, 3, 4, 5}; // Keeping this fixed throughout. @@ -203,6 +203,6 @@ TEST(ExpressionTester, checkPreBuiltElementaryOps) { // TODO: Squeeze operator. } -// TEST(ExpressionTester, checkCustomElementaryOps) { +// TEST(OperatorExpressions, checkCustomElementaryOps) { // // pass // } \ No newline at end of file diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index c68779ef45..ac8fc63887 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -100,7 +100,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { } // namespace utils_2 -// TEST(ExpressionTester, checkProductOperatorSimple) { +// TEST(OperatorExpressions, checkProductOperatorSimple) { // std::vector levels = {2, 3, 4}; // // std::set uniqueDegrees; @@ -188,7 +188,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } // } -// TEST(ExpressionTester, checkProductOperatorSimple) { +// TEST(OperatorExpressions, checkProductOperatorSimple) { // std::complex value_0 = 0.1 + 0.1; // std::complex value_1 = 0.1 + 1.0; @@ -227,7 +227,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } // } -TEST(ExpressionTester, checkOperatorSumAgainstScalarOperator) { +TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { @@ -307,7 +307,7 @@ TEST(ExpressionTester, checkOperatorSumAgainstScalarOperator) { } } -TEST(ExpressionTester, checkOperatorSumAgainstScalars) { +TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { std::complex value = 0.1 + 0.1; // `operator_sum * double` and `double * operator_sum` @@ -468,7 +468,7 @@ TEST(ExpressionTester, checkOperatorSumAgainstScalars) { } } -TEST(ExpressionTester, checkOperatorSumAgainstOperatorSum) { +TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + @@ -529,7 +529,7 @@ TEST(ExpressionTester, checkOperatorSumAgainstOperatorSum) { /// NOTE: Much of the simpler arithmetic between the two is tested in the /// product operator test file. This mainly just tests the assignment operators /// between the two types. -TEST(ExpressionTester, checkOperatorSumAgainstProduct) { +TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum += product_operator` { auto product = cudaq::elementary_operator::annihilate(0) * diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index f8b6be7742..cf4660af80 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -104,7 +104,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { /// TODO: Not yet testing the output matrices coming from this arithmetic. -TEST(ExpressionTester, checkProductOperatorSimpleMatrixChecks) { +TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector levels = {2, 3, 4}; { @@ -200,7 +200,7 @@ TEST(ExpressionTester, checkProductOperatorSimpleMatrixChecks) { } } -TEST(ExpressionTester, checkProductOperatorSimpleContinued) { +TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; @@ -244,7 +244,7 @@ TEST(ExpressionTester, checkProductOperatorSimpleContinued) { } } -TEST(ExpressionTester, checkProductOperatorAgainstScalars) { +TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::complex value_0 = 0.1 + 0.1; /// `product_operator + complex` @@ -433,7 +433,7 @@ TEST(ExpressionTester, checkProductOperatorAgainstScalars) { } } -TEST(ExpressionTester, checkProductOperatorAgainstProduct) { +TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator + product_operator` { @@ -484,7 +484,7 @@ TEST(ExpressionTester, checkProductOperatorAgainstProduct) { } } -TEST(ExpressionTester, checkProductOperatorAgainstElementary) { +TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // `product_operator + elementary_operator` { @@ -537,7 +537,7 @@ TEST(ExpressionTester, checkProductOperatorAgainstElementary) { } } -TEST(ExpressionTester, checkProductOperatorAgainstOperatorSum) { +TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator + operator_sum` { diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index a8d6054a32..edd4f9e675 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(ExpressionTester, checkScalarOpsArithmeticDoubles) { +TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { // Arithmetic overloads against complex doubles. std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; @@ -259,7 +259,7 @@ TEST(ExpressionTester, checkScalarOpsArithmeticDoubles) { } } -TEST(ExpressionTester, checkScalarOpsArithmeticScalarOps) { +TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { // Arithmetic overloads against other scalar ops. std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index c60414d705..158ab49841 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(ExpressionTester, checkScalarOpsSimpleComplex) { +TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; @@ -64,7 +64,7 @@ TEST(ExpressionTester, checkScalarOpsSimpleComplex) { } } -TEST(ExpressionTester, checkScalarOpsSimpleDouble) { +TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { double value_0 = 0.1; double value_1 = 0.2; From 93551f635be22355f3e30b369f0d675534fbaf69 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Jan 2025 20:18:50 +0000 Subject: [PATCH 047/311] a bunch of templates to draft how dragging type information through the existing operators could look like Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 168 ++++---- runtime/cudaq/dynamics/operator_sum.cpp | 200 ++++++---- runtime/cudaq/dynamics/product_operators.cpp | 116 +++--- runtime/cudaq/dynamics/scalar_operators.cpp | 35 +- runtime/cudaq/operators.h | 375 ++++++++++-------- 5 files changed, 515 insertions(+), 379 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 574891033f..fa5bc516b1 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -14,18 +14,8 @@ namespace cudaq { -/// Elementary Operator constructor. -elementary_operator::elementary_operator(std::string operator_id, - std::vector degrees) - : id(operator_id), degrees(degrees) {} -elementary_operator::elementary_operator(const elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} -elementary_operator::elementary_operator(elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} - -elementary_operator elementary_operator::identity(int degree) { +template +elementary_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -53,7 +43,8 @@ elementary_operator elementary_operator::identity(int degree) { return op; } -elementary_operator elementary_operator::zero(int degree) { +template +elementary_operator elementary_operator::zero(int degree) { std::string op_id = "zero"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -78,7 +69,8 @@ elementary_operator elementary_operator::zero(int degree) { return op; } -elementary_operator elementary_operator::annihilate(int degree) { +template +elementary_operator elementary_operator::annihilate(int degree) { std::string op_id = "annihilate"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -103,7 +95,8 @@ elementary_operator elementary_operator::annihilate(int degree) { return op; } -elementary_operator elementary_operator::create(int degree) { +template +elementary_operator elementary_operator::create(int degree) { std::string op_id = "create"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -128,7 +121,8 @@ elementary_operator elementary_operator::create(int degree) { return op; } -elementary_operator elementary_operator::position(int degree) { +template +elementary_operator elementary_operator::position(int degree) { std::string op_id = "position"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -157,7 +151,8 @@ elementary_operator elementary_operator::position(int degree) { return op; } -elementary_operator elementary_operator::momentum(int degree) { +template +elementary_operator elementary_operator::momentum(int degree) { std::string op_id = "momentum"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -186,7 +181,8 @@ elementary_operator elementary_operator::momentum(int degree) { return op; } -elementary_operator elementary_operator::number(int degree) { +template +elementary_operator elementary_operator::number(int degree) { std::string op_id = "number"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -211,7 +207,8 @@ elementary_operator elementary_operator::number(int degree) { return op; } -elementary_operator elementary_operator::parity(int degree) { +template +elementary_operator elementary_operator::parity(int degree) { std::string op_id = "parity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -236,8 +233,9 @@ elementary_operator elementary_operator::parity(int degree) { return op; } -elementary_operator -elementary_operator::displace(int degree, std::complex amplitude) { +template +elementary_operator +elementary_operator::displace(int degree, std::complex amplitude) { std::string op_id = "displace"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -274,20 +272,23 @@ elementary_operator::displace(int degree, std::complex amplitude) { return op; } -elementary_operator -elementary_operator::squeeze(int degree, std::complex amplitude) { +template +elementary_operator +elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } -matrix_2 elementary_operator::to_matrix( - const std::map dimensions, - const std::map> parameters) const { - return m_ops.at(id).generator(dimensions, parameters); +template +matrix_2 elementary_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + return m_ops[id].generator(dimensions, parameters); } /// Elementary Operator Arithmetic. -operator_sum elementary_operator::operator+(scalar_operator other) { +template +operator_sum elementary_operator::operator+(scalar_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -297,7 +298,8 @@ operator_sum elementary_operator::operator+(scalar_operator other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(scalar_operator other) { +template +operator_sum elementary_operator::operator-(scalar_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -307,13 +309,15 @@ operator_sum elementary_operator::operator-(scalar_operator other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(scalar_operator other) { +template +product_operator elementary_operator::operator*(scalar_operator other) { std::vector> _args = { *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(std::complex other) { +template +operator_sum elementary_operator::operator+(std::complex other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator(other); @@ -324,7 +328,8 @@ operator_sum elementary_operator::operator+(std::complex other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(std::complex other) { +template +operator_sum elementary_operator::operator-(std::complex other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator((-1. * other)); @@ -335,131 +340,150 @@ operator_sum elementary_operator::operator-(std::complex other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(std::complex other) { +template +product_operator elementary_operator::operator*(std::complex other) { auto other_scalar = scalar_operator(other); std::vector> _args = { *this, other_scalar}; return product_operator(_args); } -operator_sum elementary_operator::operator+(double other) { +template +operator_sum elementary_operator::operator+(double other) { std::complex value(other, 0.0); return *this + value; } -operator_sum elementary_operator::operator-(double other) { +template +operator_sum elementary_operator::operator-(double other) { std::complex value(other, 0.0); return *this - value; } -product_operator elementary_operator::operator*(double other) { +template +product_operator elementary_operator::operator*(double other) { std::complex value(other, 0.0); return *this * value; } -operator_sum operator+(std::complex other, elementary_operator self) { +template +operator_sum operator+(std::complex other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _self = { + std::vector>> _self = { self}; - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), product_operator(_self)}); } -operator_sum operator-(std::complex other, elementary_operator self) { +template +operator_sum operator-(std::complex other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } -product_operator operator*(std::complex other, - elementary_operator self) { +template +product_operator operator*(std::complex other, + elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _args = { + std::vector>> _args = { other_scalar, self}; return product_operator(_args); } -operator_sum operator+(double other, elementary_operator self) { +template +operator_sum operator+(double other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _self = { + std::vector>> _self = { self}; - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), product_operator(_self)}); } -operator_sum operator-(double other, elementary_operator self) { +template +operator_sum operator-(double other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } -product_operator operator*(double other, elementary_operator self) { +template +product_operator operator*(double other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _args = { + std::vector>> _args = { other_scalar, self}; return product_operator(_args); } -product_operator elementary_operator::operator*(elementary_operator other) { - std::vector> _args = { +template +product_operator elementary_operator::operator*(elementary_operator other) { + std::vector>> _args = { *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(elementary_operator other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator+(elementary_operator other) { + std::vector>> _this = { *this}; - std::vector> _other = { + std::vector>> _other = { other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(elementary_operator other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator-(elementary_operator other) { + std::vector>> _this = { *this}; return operator_sum({product_operator(_this), (-1. * other)}); } -operator_sum elementary_operator::operator+(operator_sum other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator+(operator_sum other) { + std::vector>> _this = { *this}; - std::vector _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum + other; } -operator_sum elementary_operator::operator-(operator_sum other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator-(operator_sum other) { + std::vector>> _this = { *this}; - std::vector _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum - other; } -operator_sum elementary_operator::operator*(operator_sum other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator*(operator_sum other) { + std::vector>> _this = { *this}; - std::vector _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum * other; } -operator_sum elementary_operator::operator+(product_operator other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator+(product_operator other) { + std::vector>> _this = { *this}; return operator_sum({product_operator(_this), other}); } -operator_sum elementary_operator::operator-(product_operator other) { +template +operator_sum elementary_operator::operator-(product_operator other) { return *this + (-1. * other); } -product_operator elementary_operator::operator*(product_operator other) { - std::vector> other_terms = +template +product_operator elementary_operator::operator*(product_operator other) { + std::vector>> other_terms = other.get_terms(); /// Insert this elementary operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index dd3227784d..48b2bd2f81 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -14,12 +14,8 @@ namespace cudaq { -/// Operator sum constructor given a vector of product operators. -operator_sum::operator_sum(const std::vector &terms) - : m_terms(terms) {} - // std::vector> -// operator_sum::canonicalize_product(product_operator &prod) const { +// operator_sum::canonicalize_product(product_operator &prod) const { // std::vector> // canonicalized_terms; @@ -41,7 +37,7 @@ operator_sum::operator_sum(const std::vector &terms) // if (all_degrees.size() == // std::set(all_degrees.begin(), all_degrees.end()).size()) { // std::sort(non_scalars.begin(), non_scalars.end(), -// [](const elementary_operator &a, const elementary_operator &b) { +// [](const elementary_operator &a, const elementary_operator &b) { // return a.degrees < b.degrees; // }); // } @@ -54,7 +50,7 @@ operator_sum::operator_sum(const std::vector &terms) // } // std::vector> -// operator_sum::_canonical_terms() const { +// operator_sum::_canonical_terms() const { // std::vector> terms; // // for (const auto &term : m_terms) { // // auto canonicalized = canonicalize_product(term); @@ -71,7 +67,7 @@ operator_sum::operator_sum(const std::vector &terms) // return terms; // } -// operator_sum operator_sum::canonicalize() const { +// operator_sum operator_sum::canonicalize() const { // std::vector canonical_terms; // for (const auto &term : _canonical_terms()) { // canonical_terms.push_back(product_operator(term)); @@ -79,12 +75,12 @@ operator_sum::operator_sum(const std::vector &terms) // return operator_sum(canonical_terms); // } -// bool operator_sum::operator==(const operator_sum &other) const { +// bool operator_sum::operator==(const operator_sum &other) const { // return _canonical_terms() == other._canonical_terms(); // } // // Degrees property -// std::vector operator_sum::degrees() const { +// std::vector operator_sum::degrees() const { // std::set unique_degrees; // for (const auto &term : m_terms) { // for (const auto &op : term.get_terms()) { @@ -97,7 +93,7 @@ operator_sum::operator_sum(const std::vector &terms) // } // // Parameters property -// std::map operator_sum::parameters() const { +// std::map operator_sum::parameters() const { // std::map param_map; // for (const auto &term : m_terms) { // for (const auto &op : term.get_terms()) { @@ -110,9 +106,9 @@ operator_sum::operator_sum(const std::vector &terms) // } // // Check if all terms are spin operators -// bool operator_sum::_is_spinop() const { +// bool operator_sum::_is_spinop() const { // return std::all_of( -// m_terms.begin(), m_terms.end(), [](product_operator &term) { +// m_terms.begin(), m_terms.end(), [](product_operator &term) { // return std::all_of(term.get_terms().begin(), // term.get_terms().end(), // [](const Operator &op) { return op.is_spinop(); @@ -121,31 +117,36 @@ operator_sum::operator_sum(const std::vector &terms) // } // Arithmetic operators -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector> combined_terms = m_terms; combined_terms.insert(combined_terms.end(), std::make_move_iterator(other.m_terms.begin()), std::make_move_iterator(other.m_terms.end())); return operator_sum(combined_terms); } -operator_sum operator_sum::operator-(const operator_sum &other) const { +template +operator_sum operator_sum::operator-(const operator_sum &other) const { return *this + (-1 * other); } -operator_sum operator_sum::operator-=(const operator_sum &other) { +template +operator_sum operator_sum::operator-=(const operator_sum &other) { *this = *this - other; return *this; } -operator_sum operator_sum::operator+=(const operator_sum &other) { +template +operator_sum operator_sum::operator+=(const operator_sum &other) { *this = *this + other; return *this; } -operator_sum operator_sum::operator*(operator_sum &other) const { +template +operator_sum operator_sum::operator*(operator_sum &other) const { auto self_terms = m_terms; - std::vector product_terms; + std::vector> product_terms; auto other_terms = other.get_terms(); for (auto &term : self_terms) { for (auto &other_term : other_terms) { @@ -155,218 +156,243 @@ operator_sum operator_sum::operator*(operator_sum &other) const { return operator_sum(product_terms); } -operator_sum operator_sum::operator*=(operator_sum &other) { +template +operator_sum operator_sum::operator*=(operator_sum &other) { *this = *this * other; return *this; } -operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator*(const scalar_operator &other) const { + std::vector> combined_terms = m_terms; for (auto &term : combined_terms) { term *= other; } return operator_sum(combined_terms); } -operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector combined_terms = m_terms; - std::vector> _other = { +template +operator_sum operator_sum::operator+(const scalar_operator &other) const { + std::vector> combined_terms = m_terms; + std::vector>> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } -operator_sum operator_sum::operator-(const scalar_operator &other) const { +template +operator_sum operator_sum::operator-(const scalar_operator &other) const { return *this + (-1.0 * other); } -operator_sum operator_sum::operator*=(const scalar_operator &other) { +template +operator_sum operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } -operator_sum operator_sum::operator+=(const scalar_operator &other) { +template +operator_sum operator_sum::operator+=(const scalar_operator &other) { *this = *this + other; return *this; } -operator_sum operator_sum::operator-=(const scalar_operator &other) { +template +operator_sum operator_sum::operator-=(const scalar_operator &other) { *this = *this - other; return *this; } -operator_sum operator_sum::operator*(std::complex other) const { +template +operator_sum operator_sum::operator*(std::complex other) const { return *this * scalar_operator(other); } -operator_sum operator_sum::operator+(std::complex other) const { +template +operator_sum operator_sum::operator+(std::complex other) const { return *this + scalar_operator(other); } -operator_sum operator_sum::operator-(std::complex other) const { +template +operator_sum operator_sum::operator-(std::complex other) const { return *this - scalar_operator(other); } -operator_sum operator_sum::operator*=(std::complex other) { +template +operator_sum operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); return *this; } -operator_sum operator_sum::operator+=(std::complex other) { +template +operator_sum operator_sum::operator+=(std::complex other) { *this += scalar_operator(other); return *this; } -operator_sum operator_sum::operator-=(std::complex other) { +template +operator_sum operator_sum::operator-=(std::complex other) { *this -= scalar_operator(other); return *this; } -operator_sum operator_sum::operator*(double other) const { +template +operator_sum operator_sum::operator*(double other) const { return *this * scalar_operator(other); } -operator_sum operator_sum::operator+(double other) const { +template +operator_sum operator_sum::operator+(double other) const { return *this + scalar_operator(other); } -operator_sum operator_sum::operator-(double other) const { +template +operator_sum operator_sum::operator-(double other) const { return *this - scalar_operator(other); } -operator_sum operator_sum::operator*=(double other) { +template +operator_sum operator_sum::operator*=(double other) { *this *= scalar_operator(other); return *this; } -operator_sum operator_sum::operator+=(double other) { +template +operator_sum operator_sum::operator+=(double other) { *this += scalar_operator(other); return *this; } -operator_sum operator_sum::operator-=(double other) { +template +operator_sum operator_sum::operator-=(double other) { *this -= scalar_operator(other); return *this; } -operator_sum operator*(std::complex other, operator_sum self) { +template +operator_sum operator*(std::complex other, operator_sum self) { return scalar_operator(other) * self; } -operator_sum operator+(std::complex other, operator_sum self) { +template +operator_sum operator+(std::complex other, operator_sum self) { return scalar_operator(other) + self; } -operator_sum operator-(std::complex other, operator_sum self) { +template +operator_sum operator-(std::complex other, operator_sum self) { return scalar_operator(other) - self; } -operator_sum operator*(double other, operator_sum self) { +template +operator_sum operator*(double other, operator_sum self) { return scalar_operator(other) * self; } -operator_sum operator+(double other, operator_sum self) { +template +operator_sum operator+(double other, operator_sum self) { return scalar_operator(other) + self; } -operator_sum operator-(double other, operator_sum self) { +template +operator_sum operator-(double other, operator_sum self) { return scalar_operator(other) - self; } -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector> combined_terms = m_terms; combined_terms.push_back(other); return operator_sum(combined_terms); } -operator_sum operator_sum::operator+=(const product_operator &other) { +template +operator_sum operator_sum::operator+=(const product_operator &other) { *this = *this + other; return *this; } -operator_sum operator_sum::operator-(const product_operator &other) const { +template +operator_sum operator_sum::operator-(const product_operator &other) const { return *this + (-1. * other); } -operator_sum operator_sum::operator-=(const product_operator &other) { +template +operator_sum operator_sum::operator-=(const product_operator &other) { *this = *this - other; return *this; } -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector> combined_terms = m_terms; for (auto &term : combined_terms) { term *= other; } return operator_sum(combined_terms); } -operator_sum operator_sum::operator*=(const product_operator &other) { +template +operator_sum operator_sum::operator*=(const product_operator &other) { *this = *this * other; return *this; } -operator_sum operator_sum::operator+(const elementary_operator &other) const { - std::vector combined_terms = m_terms; - std::vector> _other = { +template +operator_sum operator_sum::operator+(const elementary_operator &other) const { + std::vector> combined_terms = m_terms; + std::vector>> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } -operator_sum operator_sum::operator-(const elementary_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator-(const elementary_operator &other) const { + std::vector> combined_terms = m_terms; combined_terms.push_back((-1. * other)); return operator_sum(combined_terms); } -operator_sum operator_sum::operator*(const elementary_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator*(const elementary_operator &other) const { + std::vector> combined_terms = m_terms; for (auto &term : combined_terms) { term *= other; } return operator_sum(combined_terms); } -operator_sum operator_sum::operator+=(const elementary_operator &other) { - std::vector> _other = { +template +operator_sum operator_sum::operator+=(const elementary_operator &other) { + std::vector>> _other = { other}; *this = *this + product_operator(_other); return *this; } -operator_sum operator_sum::operator-=(const elementary_operator &other) { - std::vector> _other = { +template +operator_sum operator_sum::operator-=(const elementary_operator &other) { + std::vector>> _other = { other}; *this = *this - product_operator(_other); return *this; } -operator_sum operator_sum::operator*=(const elementary_operator &other) { +template +operator_sum operator_sum::operator*=(const elementary_operator &other) { *this = *this * other; return *this; } -matrix_2 operator_sum::to_matrix( - const std::map &dimensions, - const std::map> ¶ms) const { - std::size_t total_dimension = 1; - for (const auto &[_, dim] : dimensions) { - total_dimension *= dim; - } - - matrix_2 result(total_dimension, total_dimension); - - for (const auto &term : m_terms) { - matrix_2 term_matrix = term.to_matrix(dimensions, params); - - result += term_matrix; - } - - return result; -} +/// FIXME: +// tensor +// operator_sum::to_matrix(const std::map &dimensions, +// const std::map ¶ms) const { +// // todo +// } -// std::string operator_sum::to_string() const { +// std::string operator_sum::to_string() const { // std::string result; // // for (const auto &term : m_terms) { // // result += term.to_string() + " + "; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 32adcaa339..5441c38bcb 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -16,12 +16,6 @@ namespace cudaq { -/// Product Operator constructors. -product_operator::product_operator( - std::vector> - atomic_operators) - : m_terms(atomic_operators) {} - // tensor kroneckerHelper(std::vector &matrices) { // // essentially we pass in the list of elementary operators to // // this function -- with lowest degree being leftmost -- then it computes @@ -83,7 +77,7 @@ matrix_2 product_operator::to_matrix( } // /// IMPLEMENT: -// tensor product_operator::to_matrix( +// tensor product_operator::to_matrix( // std::map dimensions, // std::map> parameters) { @@ -149,7 +143,8 @@ matrix_2 product_operator::to_matrix( // } // Degrees property -std::vector product_operator::degrees() const { +template +std::vector product_operator::degrees() const { std::set unique_degrees; // The variant type makes it difficult auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; @@ -166,98 +161,119 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } -operator_sum product_operator::operator+(scalar_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator+(scalar_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, product_operator(_other)}); } -operator_sum product_operator::operator-(scalar_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator-(scalar_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, -1. * product_operator(_other)}); } -product_operator product_operator::operator*(scalar_operator other) { - std::vector> +template +product_operator product_operator::operator*(scalar_operator other) { + std::vector>> combined_terms = m_terms; combined_terms.push_back(other); return product_operator(combined_terms); } -product_operator product_operator::operator*=(scalar_operator other) { +template +product_operator product_operator::operator*=(scalar_operator other) { *this = *this * other; return *this; } -operator_sum product_operator::operator+(std::complex other) { +template +operator_sum product_operator::operator+(std::complex other) { return *this + scalar_operator(other); } -operator_sum product_operator::operator-(std::complex other) { +template +operator_sum product_operator::operator-(std::complex other) { return *this - scalar_operator(other); } -product_operator product_operator::operator*(std::complex other) { +template +product_operator product_operator::operator*(std::complex other) { return *this * scalar_operator(other); } -product_operator product_operator::operator*=(std::complex other) { +template +product_operator product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } -operator_sum operator+(std::complex other, product_operator self) { +template +operator_sum operator+(std::complex other, product_operator self) { return operator_sum({scalar_operator(other), self}); } -operator_sum operator-(std::complex other, product_operator self) { +template +operator_sum operator-(std::complex other, product_operator self) { return scalar_operator(other) - self; } -product_operator operator*(std::complex other, product_operator self) { +template +product_operator operator*(std::complex other, product_operator self) { return scalar_operator(other) * self; } -operator_sum product_operator::operator+(double other) { +template +operator_sum product_operator::operator+(double other) { return *this + scalar_operator(other); } -operator_sum product_operator::operator-(double other) { +template +operator_sum product_operator::operator-(double other) { return *this - scalar_operator(other); } -product_operator product_operator::operator*(double other) { +template +product_operator product_operator::operator*(double other) { return *this * scalar_operator(other); } -product_operator product_operator::operator*=(double other) { +template +product_operator product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } -operator_sum operator+(double other, product_operator self) { +template +operator_sum operator+(double other, product_operator self) { return operator_sum({scalar_operator(other), self}); } -operator_sum operator-(double other, product_operator self) { +template +operator_sum operator-(double other, product_operator self) { return scalar_operator(other) - self; } -product_operator operator*(double other, product_operator self) { +template +product_operator operator*(double other, product_operator self) { return scalar_operator(other) * self; } -operator_sum product_operator::operator+(product_operator other) { +template +operator_sum product_operator::operator+(product_operator other) { return operator_sum({*this, other}); } -operator_sum product_operator::operator-(product_operator other) { +template +operator_sum product_operator::operator-(product_operator other) { return operator_sum({*this, (-1. * other)}); } -product_operator product_operator::operator*(product_operator other) { - std::vector> +template +product_operator product_operator::operator*(product_operator other) { + std::vector>> combined_terms = m_terms; combined_terms.insert(combined_terms.end(), std::make_move_iterator(other.m_terms.begin()), @@ -265,50 +281,58 @@ product_operator product_operator::operator*(product_operator other) { return product_operator(combined_terms); } -product_operator product_operator::operator*=(product_operator other) { +template +product_operator product_operator::operator*=(product_operator other) { *this = *this * other; return *this; } -operator_sum product_operator::operator+(elementary_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator+(elementary_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, product_operator(_other)}); } -operator_sum product_operator::operator-(elementary_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator-(elementary_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, -1. * product_operator(_other)}); } -product_operator product_operator::operator*(elementary_operator other) { - std::vector> +template +product_operator product_operator::operator*(elementary_operator other) { + std::vector>> combined_terms = m_terms; combined_terms.push_back(other); return product_operator(combined_terms); } -product_operator product_operator::operator*=(elementary_operator other) { +template +product_operator product_operator::operator*=(elementary_operator other) { *this = *this * other; return *this; } -operator_sum product_operator::operator+(operator_sum other) { +template +operator_sum product_operator::operator+(operator_sum other) { std::vector other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum product_operator::operator-(operator_sum other) { +template +operator_sum product_operator::operator-(operator_sum other) { auto negative_other = (-1. * other); - std::vector other_terms = negative_other.get_terms(); + std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum product_operator::operator*(operator_sum other) { - std::vector other_terms = other.get_terms(); +template +operator_sum product_operator::operator*(operator_sum other) { + std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) { term = *this * term; } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 4d640b50b5..d8a68bb86b 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -219,54 +219,63 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); -operator_sum scalar_operator::operator+(elementary_operator other) { +template +operator_sum scalar_operator::operator+(elementary_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum({product_operator({*this}), product_operator({other})}); } -operator_sum scalar_operator::operator-(elementary_operator other) { +template +operator_sum scalar_operator::operator-(elementary_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum( {product_operator({*this}), product_operator({-1. * other})}); } -product_operator scalar_operator::operator*(elementary_operator other) { +template +product_operator scalar_operator::operator*(elementary_operator other) { return product_operator({*this, other}); } -operator_sum scalar_operator::operator+(product_operator other) { +template +operator_sum scalar_operator::operator+(product_operator other) { return operator_sum({product_operator({*this}), other}); } -operator_sum scalar_operator::operator-(product_operator other) { +template +operator_sum scalar_operator::operator-(product_operator other) { return operator_sum({product_operator({*this}), (-1. * other)}); } -product_operator scalar_operator::operator*(product_operator other) { - std::vector> other_terms = +template +product_operator scalar_operator::operator*(product_operator other) { + std::vector>> other_terms = other.get_terms(); /// Insert this scalar operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); return product_operator(other_terms); } -operator_sum scalar_operator::operator+(operator_sum other) { - std::vector other_terms = other.get_terms(); +template +operator_sum scalar_operator::operator+(operator_sum other) { + std::vector> other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum scalar_operator::operator-(operator_sum other) { +template +operator_sum scalar_operator::operator-(operator_sum other) { auto negative_other = (-1. * other); - std::vector other_terms = negative_other.get_terms(); + std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum scalar_operator::operator*(operator_sum other) { - std::vector other_terms = other.get_terms(); +template +operator_sum scalar_operator::operator*(operator_sum other) { + std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) term = *this * term; return operator_sum(other_terms); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6543c5fb72..f5e4dca422 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -18,36 +18,45 @@ namespace cudaq { +template class operator_sum; + +template class product_operator; -class scalar_operator; + +template class elementary_operator; +class scalar_operator; + /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. +template // handler needs to inherit from operation_handler class operator_sum { + private: - std::vector m_terms; + std::vector> m_terms; - std::vector> - canonicalize_product(product_operator &prod) const; + std::vector>> + canonicalize_product(product_operator &prod) const; - std::vector> + std::vector>> _canonical_terms() const; public: /// @brief Empty constructor that a user can aggregate terms into. - operator_sum() = default; + // operator_sum() = default; // FIXME: NEEDED? - /// @brief Construct a `cudaq::operator_sum` given a sequence of - /// `cudaq::product_operator`'s. + /// @brief Construct a `cudaq::operator_sum` given a sequence of + /// `cudaq::product_operator`'s. /// This operator expression represents a sum of terms, where each term /// is a product of elementary and scalar operators. - operator_sum(const std::vector &terms); + operator_sum(const std::vector> &terms) + : m_terms(terms) {} - operator_sum canonicalize() const; + operator_sum canonicalize() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. @@ -59,7 +68,7 @@ class operator_sum { // template // TEval _evaluate(OperatorArithmetics &arithmetics) const; - /// @brief Return the `operator_sum` as a matrix. + /// @brief Return the `operator_sum` as a matrix. /// @arg `dimensions` : A mapping that specifies the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -71,50 +80,50 @@ class operator_sum { const std::map> ¶ms = {}) const; // Arithmetic operators - operator_sum operator+(const operator_sum &other) const; - operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(operator_sum &other) const; - operator_sum operator*=(operator_sum &other); - operator_sum operator+=(const operator_sum &other); - operator_sum operator-=(const operator_sum &other); - operator_sum operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*=(const scalar_operator &other); - operator_sum operator+=(const scalar_operator &other); - operator_sum operator-=(const scalar_operator &other); - operator_sum operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - operator_sum operator*=(std::complex other); - operator_sum operator+=(std::complex other); - operator_sum operator-=(std::complex other); - operator_sum operator*(double other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - operator_sum operator*=(double other); - operator_sum operator+=(double other); - operator_sum operator-=(double other); - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; - operator_sum operator*=(const product_operator &other); - operator_sum operator+=(const product_operator &other); - operator_sum operator-=(const product_operator &other); - operator_sum operator+(const elementary_operator &other) const; - operator_sum operator-(const elementary_operator &other) const; - operator_sum operator*(const elementary_operator &other) const; - operator_sum operator*=(const elementary_operator &other); - operator_sum operator+=(const elementary_operator &other); - operator_sum operator-=(const elementary_operator &other); - - /// @brief Return the operator_sum as a string. + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; + operator_sum operator*(operator_sum &other) const; + operator_sum operator*=(operator_sum &other); + operator_sum operator+=(const operator_sum &other); + operator_sum operator-=(const operator_sum &other); + operator_sum operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + operator_sum operator*=(const scalar_operator &other); + operator_sum operator+=(const scalar_operator &other); + operator_sum operator-=(const scalar_operator &other); + operator_sum operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + operator_sum operator*=(std::complex other); + operator_sum operator+=(std::complex other); + operator_sum operator-=(std::complex other); + operator_sum operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + operator_sum operator*=(double other); + operator_sum operator+=(double other); + operator_sum operator-=(double other); + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + operator_sum operator*=(const product_operator &other); + operator_sum operator+=(const product_operator &other); + operator_sum operator-=(const product_operator &other); + operator_sum operator+(const elementary_operator &other) const; + operator_sum operator-(const elementary_operator &other) const; + operator_sum operator*(const elementary_operator &other) const; + operator_sum operator*=(const elementary_operator &other); + operator_sum operator+=(const elementary_operator &other); + operator_sum operator-=(const elementary_operator &other); + + /// @brief Return the operator_sum as a string. std::string to_string() const; /// @brief Return the number of operator terms that make up this operator sum. int term_count() const { return m_terms.size(); } - /// @brief True, if the other value is an operator_sum with equivalent terms, + /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. @@ -123,29 +132,38 @@ class operator_sum { /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. - bool operator==(const operator_sum &other) const; + bool operator==(const operator_sum &other) const; /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector get_terms() const { return m_terms; } + std::vector> get_terms() { return m_terms; } }; -operator_sum operator*(std::complex other, operator_sum self); -operator_sum operator+(std::complex other, operator_sum self); -operator_sum operator-(std::complex other, operator_sum self); -operator_sum operator*(double other, operator_sum self); -operator_sum operator+(double other, operator_sum self); -operator_sum operator-(double other, operator_sum self); + +template +operator_sum operator*(std::complex other, operator_sum self); +template +operator_sum operator+(std::complex other, operator_sum self); +template +operator_sum operator-(std::complex other, operator_sum self); +template +operator_sum operator*(double other, operator_sum self); +template +operator_sum operator+(double other, operator_sum self); +template +operator_sum operator-(double other, operator_sum self); /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types /// that can. -class product_operator : public operator_sum { +template // handler needs to inherit from operation_handler +class product_operator : public operator_sum { + private: - std::vector> m_terms; + std::vector>> m_terms; public: - product_operator() = default; + // product_operator() = default; // FIXME: needed? ~product_operator() = default; // Constructor for an operator expression that represents a product @@ -153,35 +171,36 @@ class product_operator : public operator_sum { // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. product_operator( - std::vector> - atomic_operators); + std::vector>> + atomic_operators) + : m_terms(atomic_operators) {} // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - product_operator operator*=(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); - product_operator operator*=(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); - product_operator operator*=(scalar_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - product_operator operator*=(product_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - product_operator operator*=(elementary_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator*(operator_sum other); - - /// @brief True, if the other value is an operator_sum with equivalent terms, + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + product_operator operator*=(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + product_operator operator*=(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + product_operator operator*=(scalar_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + product_operator operator*=(product_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + product_operator operator*=(elementary_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. @@ -190,12 +209,12 @@ class product_operator : public operator_sum { /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. - bool operator==(product_operator other); + bool operator==(product_operator other); - /// @brief Return the `product_operator` as a string. + /// @brief Return the `product_operator` as a string. std::string to_string() const; - /// @brief Return the `operator_sum` as a matrix. + /// @brief Return the `operator_sum` as a matrix. /// @arg `dimensions` : A mapping that specifies the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -220,65 +239,82 @@ class product_operator : public operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> - get_terms() const { + std::vector>> get_terms() { return m_terms; }; }; -operator_sum operator+(std::complex other, product_operator self); -operator_sum operator-(std::complex other, product_operator self); -product_operator operator*(std::complex other, product_operator self); -operator_sum operator+(double other, product_operator self); -operator_sum operator-(double other, product_operator self); -product_operator operator*(double other, product_operator self); - -class elementary_operator : public product_operator { + +template +operator_sum operator+(std::complex other, product_operator self); +template +operator_sum operator-(std::complex other, product_operator self); +template +product_operator operator*(std::complex other, product_operator self); +template +operator_sum operator+(double other, product_operator self); +template +operator_sum operator-(double other, product_operator self); +template +product_operator operator*(double other, product_operator self); + +template +class elementary_operator : public product_operator { + private: std::map m_ops; public: // The constructor should never be called directly by the user: - // Keeping it internally documentd for now, however. + // Keeping it internally documented for now, however. /// @brief Constructor. /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - elementary_operator(std::string operator_id, std::vector degrees); + elementary_operator(std::string operator_id, std::vector degrees) + : id(operator_id), degrees(degrees) {} - // Copy constructor. - elementary_operator(const elementary_operator &other); - elementary_operator(elementary_operator &other); + // Copy constructor. FIXME: NEEDED? + elementary_operator(const elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} + + elementary_operator(elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator+=(operator_sum other); - operator_sum operator-=(operator_sum other); - operator_sum operator*(operator_sum other); + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + // big question regarding templates is whether coefficients are part of product or are part of elem ops - + // part of product seems more intuitive to me, and probably simplifies code; + // that would mean even for eager prod/add, we still get a prod/sum as result + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator+=(operator_sum other); + operator_sum operator-=(operator_sum other); + operator_sum operator*(operator_sum other); /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + bool operator==(elementary_operator other); - /// @brief Return the `elementary_operator` as a string. + /// @brief Return the `elementary_operator` as a string. std::string to_string() const; - /// @brief Return the `elementary_operator` as a matrix. + /// @brief Return the `elementary_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -288,18 +324,18 @@ class elementary_operator : public product_operator { const std::map> parameters) const; // Predefined operators. - static elementary_operator identity(int degree); - static elementary_operator zero(int degree); - static elementary_operator annihilate(int degree); - static elementary_operator create(int degree); - static elementary_operator momentum(int degree); - static elementary_operator number(int degree); - static elementary_operator parity(int degree); - static elementary_operator position(int degree); + static elementary_operator identity(int degree); + static elementary_operator zero(int degree); + static elementary_operator annihilate(int degree); + static elementary_operator create(int degree); + static elementary_operator momentum(int degree); + static elementary_operator number(int degree); + static elementary_operator parity(int degree); + static elementary_operator position(int degree); /// FIXME: - static elementary_operator squeeze(int degree, + static elementary_operator squeeze(int degree, std::complex amplitude); - static elementary_operator displace(int degree, + static elementary_operator displace(int degree, std::complex amplitude); /// @brief Adds the definition of an elementary operator with the given id to @@ -357,15 +393,24 @@ class elementary_operator : public product_operator { // pauli_word to_pauli_word ovveride(); }; // Reverse order arithmetic for elementary operators against pure scalars. -operator_sum operator+(std::complex other, elementary_operator self); -operator_sum operator-(std::complex other, elementary_operator self); -product_operator operator*(std::complex other, - elementary_operator self); -operator_sum operator+(double other, elementary_operator self); -operator_sum operator-(double other, elementary_operator self); -product_operator operator*(double other, elementary_operator self); - -class scalar_operator : public product_operator { +template +operator_sum operator+(std::complex other, elementary_operator self); +template +operator_sum operator-(std::complex other, elementary_operator self); +template +product_operator operator*(std::complex other, + elementary_operator self); +template +operator_sum operator+(double other, elementary_operator self); +template +operator_sum operator-(double other, elementary_operator self); +template +product_operator operator*(double other, elementary_operator self); + +// FIXME: check if we really need the inheritance from prod operator, and if so what it should be +// (check if this really should be its own class?) +class scalar_operator { + private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. @@ -392,25 +437,33 @@ class scalar_operator : public product_operator { scalar_operator operator/(scalar_operator other); /// TODO: implement and test pow scalar_operator pow(scalar_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator*(operator_sum other); + template + operator_sum operator+(elementary_operator other); + template + operator_sum operator-(elementary_operator other); + template + product_operator operator*(elementary_operator other); + template + operator_sum operator+(product_operator other); + template + operator_sum operator-(product_operator other); + template + product_operator operator*(product_operator other); + template + operator_sum operator+(operator_sum other); + template + operator_sum operator-(operator_sum other); + template + operator_sum operator*(operator_sum other); /// @brief Return the scalar operator as a concrete complex value. std::complex evaluate(std::map> parameters) const; // Return the scalar operator as a 1x1 matrix. This is needed for - // compatability with the other inherited classes. - matrix_2 - to_matrix(const std::map dimensions, - const std::map> parameters) const; + // compatibility with the other inherited classes. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); // /// @brief Returns true if other is a scalar operator with the same // /// generator. From 11df37be2f752bb1c47692bbebb56f148f1a4b26 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 19:34:05 +0000 Subject: [PATCH 048/311] wip to use elementary_operator as matrix handler Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 149 +++--- runtime/cudaq/dynamics/operator_sum.cpp | 80 ++-- runtime/cudaq/dynamics/product_operators.cpp | 108 ++--- runtime/cudaq/dynamics/scalar_operators.cpp | 242 ++++------ runtime/cudaq/operators.h | 449 +++++++++--------- 5 files changed, 481 insertions(+), 547 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index fa5bc516b1..baa5df8396 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -10,12 +10,12 @@ #include "cudaq/operators.h" #include +#include #include namespace cudaq { -template -elementary_operator elementary_operator::identity(int degree) { +elementary_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -43,8 +43,7 @@ elementary_operator elementary_operator::identity(int degr return op; } -template -elementary_operator elementary_operator::zero(int degree) { +elementary_operator elementary_operator::zero(int degree) { std::string op_id = "zero"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -69,8 +68,7 @@ elementary_operator elementary_operator::zero(int degree) return op; } -template -elementary_operator elementary_operator::annihilate(int degree) { +elementary_operator elementary_operator::annihilate(int degree) { std::string op_id = "annihilate"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -95,8 +93,7 @@ elementary_operator elementary_operator::annihilate(int de return op; } -template -elementary_operator elementary_operator::create(int degree) { +elementary_operator elementary_operator::create(int degree) { std::string op_id = "create"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -121,8 +118,7 @@ elementary_operator elementary_operator::create(int degree return op; } -template -elementary_operator elementary_operator::position(int degree) { +elementary_operator elementary_operator::position(int degree) { std::string op_id = "position"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -151,8 +147,7 @@ elementary_operator elementary_operator::position(int degr return op; } -template -elementary_operator elementary_operator::momentum(int degree) { +elementary_operator elementary_operator::momentum(int degree) { std::string op_id = "momentum"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -181,8 +176,7 @@ elementary_operator elementary_operator::momentum(int degr return op; } -template -elementary_operator elementary_operator::number(int degree) { +elementary_operator elementary_operator::number(int degree) { std::string op_id = "number"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -207,8 +201,7 @@ elementary_operator elementary_operator::number(int degree return op; } -template -elementary_operator elementary_operator::parity(int degree) { +elementary_operator elementary_operator::parity(int degree) { std::string op_id = "parity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -233,9 +226,8 @@ elementary_operator elementary_operator::parity(int degree return op; } -template -elementary_operator -elementary_operator::displace(int degree, std::complex amplitude) { +elementary_operator +elementary_operator::displace(int degree, std::complex amplitude) { std::string op_id = "displace"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -272,14 +264,12 @@ elementary_operator::displace(int degree, std::complex amplit return op; } -template -elementary_operator -elementary_operator::squeeze(int degree, std::complex amplitude) { +elementary_operator +elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } -template -matrix_2 elementary_operator::to_matrix( +matrix_2 elementary_operator::to_matrix( std::map dimensions, std::map> parameters) { return m_ops[id].generator(dimensions, parameters); @@ -287,8 +277,7 @@ matrix_2 elementary_operator::to_matrix( /// Elementary Operator Arithmetic. -template -operator_sum elementary_operator::operator+(scalar_operator other) { +operator_sum elementary_operator::operator+(const scalar_operator other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -298,8 +287,7 @@ operator_sum elementary_operator::operator+(scalar_operato return operator_sum({product_operator(_this), product_operator(_other)}); } -template -operator_sum elementary_operator::operator-(scalar_operator other) { +operator_sum elementary_operator::operator-(const scalar_operator other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -309,15 +297,13 @@ operator_sum elementary_operator::operator-(scalar_operato return operator_sum({product_operator(_this), product_operator(_other)}); } -template -product_operator elementary_operator::operator*(scalar_operator other) { +product_operator elementary_operator::operator*(const scalar_operator other) const { std::vector> _args = { *this, other}; return product_operator(_args); } -template -operator_sum elementary_operator::operator+(std::complex other) { +operator_sum elementary_operator::operator+(const std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator(other); @@ -328,8 +314,7 @@ operator_sum elementary_operator::operator+(std::complex -operator_sum elementary_operator::operator-(std::complex other) { +operator_sum elementary_operator::operator-(const std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator((-1. * other)); @@ -340,150 +325,136 @@ operator_sum elementary_operator::operator-(std::complex -product_operator elementary_operator::operator*(std::complex other) { +product_operator elementary_operator::operator*(const std::complex other) const { auto other_scalar = scalar_operator(other); std::vector> _args = { *this, other_scalar}; return product_operator(_args); } -template -operator_sum elementary_operator::operator+(double other) { +operator_sum elementary_operator::operator+(double other) const { std::complex value(other, 0.0); return *this + value; } -template -operator_sum elementary_operator::operator-(double other) { +operator_sum elementary_operator::operator-(double other) const { std::complex value(other, 0.0); return *this - value; } -template -product_operator elementary_operator::operator*(double other) { +product_operator elementary_operator::operator*(double other) const { std::complex value(other, 0.0); return *this * value; } -template -operator_sum operator+(std::complex other, elementary_operator self) { +operator_sum operator+(const std::complex other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _self = { + std::vector> _self = { self}; - std::vector>> _other = { + std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum({product_operator(_other), product_operator(_self)}); } template -operator_sum operator-(std::complex other, elementary_operator self) { +operator_sum operator-(const std::complex other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _other = { + std::vector> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } template -product_operator operator*(std::complex other, - elementary_operator self) { +product_operator operator*(const std::complex other, + const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _args = { + std::vector> _args = { other_scalar, self}; return product_operator(_args); } template -operator_sum operator+(double other, elementary_operator self) { +operator_sum operator+(double other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _self = { + std::vector> _self = { self}; - std::vector>> _other = { + std::vector> _other = { other_scalar}; return operator_sum({product_operator(_other), product_operator(_self)}); } template -operator_sum operator-(double other, elementary_operator self) { +operator_sum operator-(double other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _other = { + std::vector> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } template -product_operator operator*(double other, elementary_operator self) { +product_operator operator*(double other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _args = { + std::vector> _args = { other_scalar, self}; return product_operator(_args); } -template -product_operator elementary_operator::operator*(elementary_operator other) { - std::vector>> _args = { +product_operator elementary_operator::operator*(const elementary_operator other) const { + std::vector> _args = { *this, other}; return product_operator(_args); } -template -operator_sum elementary_operator::operator+(elementary_operator other) { - std::vector>> _this = { +operator_sum elementary_operator::operator+(const elementary_operator other) const { + std::vector> _this = { *this}; - std::vector>> _other = { + std::vector> _other = { other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -template -operator_sum elementary_operator::operator-(elementary_operator other) { - std::vector>> _this = { +operator_sum elementary_operator::operator-(const elementary_operator other) const { + std::vector> _this = { *this}; return operator_sum({product_operator(_this), (-1. * other)}); } -template -operator_sum elementary_operator::operator+(operator_sum other) { - std::vector>> _this = { +operator_sum elementary_operator::operator+(const operator_sum other) const { + std::vector> _this = { *this}; - std::vector> _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum + other; } -template -operator_sum elementary_operator::operator-(operator_sum other) { - std::vector>> _this = { +operator_sum elementary_operator::operator-(const operator_sum other) const { + std::vector> _this = { *this}; - std::vector> _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum - other; } -template -operator_sum elementary_operator::operator*(operator_sum other) { - std::vector>> _this = { +operator_sum elementary_operator::operator*(const operator_sum other) const { + std::vector> _this = { *this}; - std::vector> _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum * other; } -template -operator_sum elementary_operator::operator+(product_operator other) { - std::vector>> _this = { +operator_sum elementary_operator::operator+(const product_operator other) const { + std::vector> _this = { *this}; return operator_sum({product_operator(_this), other}); } -template -operator_sum elementary_operator::operator-(product_operator other) { +operator_sum elementary_operator::operator-(const product_operator other) const { return *this + (-1. * other); } -template -product_operator elementary_operator::operator*(product_operator other) { - std::vector>> other_terms = +product_operator elementary_operator::operator*(const product_operator other) const { + std::vector> other_terms = other.get_terms(); /// Insert this elementary operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 48b2bd2f81..e3a3e97e45 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -14,30 +14,30 @@ namespace cudaq { -// std::vector> +// std::vector> // operator_sum::canonicalize_product(product_operator &prod) const { -// std::vector> +// std::vector> // canonicalized_terms; // std::vector all_degrees; // std::vector scalars; -// std::vector non_scalars; +// std::vector non_scalars; // for (const auto &op : prod.get_terms()) { // if (std::holds_alternative(op)) { // scalars.push_back(*std::get(op)); // } else { -// non_scalars.push_back(*std::get(op)); +// non_scalars.push_back(*std::get(op)); // all_degrees.insert(all_degrees.end(), -// std::get(op).degrees.begin(), -// std::get(op).degrees.end()); +// std::get(op).degrees.begin(), +// std::get(op).degrees.end()); // } // } // if (all_degrees.size() == // std::set(all_degrees.begin(), all_degrees.end()).size()) { // std::sort(non_scalars.begin(), non_scalars.end(), -// [](const elementary_operator &a, const elementary_operator &b) { +// [](const HandlerTy &a, const HandlerTy &b) { // return a.degrees < b.degrees; // }); // } @@ -49,10 +49,10 @@ namespace cudaq { // return canonicalized_terms; // } -// std::vector> +// std::vector> // operator_sum::_canonical_terms() const { -// std::vector> terms; -// // for (const auto &term : m_terms) { +// std::vector> terms; +// // for (const auto &term : terms) { // // auto canonicalized = canonicalize_product(term); // // terms.insert(terms.end(), canonicalized.begin(), canonicalized.end()); // // } @@ -82,7 +82,7 @@ namespace cudaq { // // Degrees property // std::vector operator_sum::degrees() const { // std::set unique_degrees; -// for (const auto &term : m_terms) { +// for (const auto &term : terms) { // for (const auto &op : term.get_terms()) { // unique_degrees.insert(op.get_degrees().begin(), // op.get_degrees().end()); @@ -95,7 +95,7 @@ namespace cudaq { // // Parameters property // std::map operator_sum::parameters() const { // std::map param_map; -// for (const auto &term : m_terms) { +// for (const auto &term : terms) { // for (const auto &op : term.get_terms()) { // auto op_params = op.parameters(); // param_map.insert(op_params.begin(), op.params.end()); @@ -108,7 +108,7 @@ namespace cudaq { // // Check if all terms are spin operators // bool operator_sum::_is_spinop() const { // return std::all_of( -// m_terms.begin(), m_terms.end(), [](product_operator &term) { +// terms.begin(), terms.end(), [](product_operator &term) { // return std::all_of(term.get_terms().begin(), // term.get_terms().end(), // [](const Operator &op) { return op.is_spinop(); @@ -116,13 +116,15 @@ namespace cudaq { // }); // } +template class operator_sum; + // Arithmetic operators template operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.m_terms.begin()), - std::make_move_iterator(other.m_terms.end())); + std::make_move_iterator(other.terms.begin()), + std::make_move_iterator(other.terms.end())); return operator_sum(combined_terms); } @@ -131,6 +133,10 @@ operator_sum operator_sum::operator-(const operator_sum operator_sum::operator-( +// const operator_sum &other) const; + template operator_sum operator_sum::operator-=(const operator_sum &other) { *this = *this - other; @@ -144,8 +150,8 @@ operator_sum operator_sum::operator+=(const operator_sum -operator_sum operator_sum::operator*(operator_sum &other) const { - auto self_terms = m_terms; +operator_sum operator_sum::operator*(const operator_sum &other) const { + auto self_terms = terms; std::vector> product_terms; auto other_terms = other.get_terms(); for (auto &term : self_terms) { @@ -157,14 +163,14 @@ operator_sum operator_sum::operator*(operator_sum -operator_sum operator_sum::operator*=(operator_sum &other) { +operator_sum operator_sum::operator*=(const operator_sum &other) { *this = *this * other; return *this; } template operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; for (auto &term : combined_terms) { term *= other; } @@ -173,8 +179,8 @@ operator_sum operator_sum::operator*(const scalar_operator template operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = m_terms; - std::vector>> _other = { + std::vector> combined_terms = terms; + std::vector> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); @@ -301,7 +307,7 @@ operator_sum operator-(double other, operator_sum self) { template operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; combined_terms.push_back(other); return operator_sum(combined_terms); } @@ -325,7 +331,7 @@ operator_sum operator_sum::operator-=(const product_operat template operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; for (auto &term : combined_terms) { term *= other; } @@ -339,24 +345,24 @@ operator_sum operator_sum::operator*=(const product_operat } template -operator_sum operator_sum::operator+(const elementary_operator &other) const { - std::vector> combined_terms = m_terms; - std::vector>> _other = { +operator_sum operator_sum::operator+(const HandlerTy &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } template -operator_sum operator_sum::operator-(const elementary_operator &other) const { - std::vector> combined_terms = m_terms; +operator_sum operator_sum::operator-(const HandlerTy &other) const { + std::vector> combined_terms = terms; combined_terms.push_back((-1. * other)); return operator_sum(combined_terms); } template -operator_sum operator_sum::operator*(const elementary_operator &other) const { - std::vector> combined_terms = m_terms; +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> combined_terms = terms; for (auto &term : combined_terms) { term *= other; } @@ -364,23 +370,23 @@ operator_sum operator_sum::operator*(const elementary_oper } template -operator_sum operator_sum::operator+=(const elementary_operator &other) { - std::vector>> _other = { +operator_sum operator_sum::operator+=(const HandlerTy &other) { + std::vector> _other = { other}; *this = *this + product_operator(_other); return *this; } template -operator_sum operator_sum::operator-=(const elementary_operator &other) { - std::vector>> _other = { +operator_sum operator_sum::operator-=(const HandlerTy &other) { + std::vector> _other = { other}; *this = *this - product_operator(_other); return *this; } template -operator_sum operator_sum::operator*=(const elementary_operator &other) { +operator_sum operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } @@ -394,7 +400,7 @@ operator_sum operator_sum::operator*=(const elementary_ope // std::string operator_sum::to_string() const { // std::string result; -// // for (const auto &term : m_terms) { +// // for (const auto &term : terms) { // // result += term.to_string() + " + "; // // } // // // Remove last " + " diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 5441c38bcb..293fb2cfc1 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -100,7 +100,7 @@ matrix_2 product_operator::to_matrix( // return outMatrix; // }; // std::vector matricesFullVectorSpace; -// for (auto &term : m_terms) { +// for (auto &term : ops) { // auto op_degrees = std::visit(getDegrees, term); // std::cout << "here 58\n"; // // Keeps track of if we've already inserted the operator matrix @@ -142,6 +142,8 @@ matrix_2 product_operator::to_matrix( // return out; // } +template class product_operator; + // Degrees property template std::vector product_operator::degrees() const { @@ -149,7 +151,7 @@ std::vector product_operator::degrees() const { // The variant type makes it difficult auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; auto endFunc = [](auto &&t) { return t.degrees.end(); }; - for (const auto &term : m_terms) { + for (const auto &term : ops) { unique_degrees.insert(std::visit(beginFunc, term), std::visit(endFunc, term)); } @@ -162,81 +164,81 @@ std::vector product_operator::degrees() const { } template -operator_sum product_operator::operator+(scalar_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator+(const scalar_operator other) const { + std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum({*this, product_operator(_other)}); } template -operator_sum product_operator::operator-(scalar_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator-(const scalar_operator other) const { + std::vector> _other = { other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, -1. * product_operator(_other)}); } template -product_operator product_operator::operator*(scalar_operator other) { - std::vector>> - combined_terms = m_terms; +product_operator product_operator::operator*(const scalar_operator other) const { + std::vector> + combined_terms = ops; combined_terms.push_back(other); return product_operator(combined_terms); } template -product_operator product_operator::operator*=(scalar_operator other) { +product_operator product_operator::operator*=(const scalar_operator other) { *this = *this * other; return *this; } template -operator_sum product_operator::operator+(std::complex other) { +operator_sum product_operator::operator+(const std::complex other) const { return *this + scalar_operator(other); } template -operator_sum product_operator::operator-(std::complex other) { +operator_sum product_operator::operator-(const std::complex other) const { return *this - scalar_operator(other); } template -product_operator product_operator::operator*(std::complex other) { +product_operator product_operator::operator*(const std::complex other) const { return *this * scalar_operator(other); } template -product_operator product_operator::operator*=(std::complex other) { +product_operator product_operator::operator*=(const std::complex other) { *this = *this * scalar_operator(other); return *this; } template -operator_sum operator+(std::complex other, product_operator self) { - return operator_sum({scalar_operator(other), self}); +operator_sum operator+(const std::complex other, const product_operator self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum operator-(std::complex other, product_operator self) { +operator_sum operator-(const std::complex other, const product_operator self) { return scalar_operator(other) - self; } template -product_operator operator*(std::complex other, product_operator self) { +product_operator operator*(const std::complex other, const product_operator self) { return scalar_operator(other) * self; } template -operator_sum product_operator::operator+(double other) { +operator_sum product_operator::operator+(double other) const { return *this + scalar_operator(other); } template -operator_sum product_operator::operator-(double other) { +operator_sum product_operator::operator-(double other) const { return *this - scalar_operator(other); } template -product_operator product_operator::operator*(double other) { +product_operator product_operator::operator*(double other) const { return *this * scalar_operator(other); } @@ -247,96 +249,96 @@ product_operator product_operator::operator*=(double other } template -operator_sum operator+(double other, product_operator self) { - return operator_sum({scalar_operator(other), self}); +operator_sum operator+(double other, const product_operator self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum operator-(double other, product_operator self) { +operator_sum operator-(double other, const product_operator self) { return scalar_operator(other) - self; } template -product_operator operator*(double other, product_operator self) { +product_operator operator*(double other, const product_operator self) { return scalar_operator(other) * self; } template -operator_sum product_operator::operator+(product_operator other) { - return operator_sum({*this, other}); +operator_sum product_operator::operator+(const product_operator other) const { + return operator_sum({*this, other}); } template -operator_sum product_operator::operator-(product_operator other) { - return operator_sum({*this, (-1. * other)}); +operator_sum product_operator::operator-(const product_operator other) const { + return operator_sum({*this, (-1. * other)}); } template -product_operator product_operator::operator*(product_operator other) { - std::vector>> - combined_terms = m_terms; +product_operator product_operator::operator*(const product_operator other) const { + std::vector> + combined_terms = ops; combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.m_terms.begin()), - std::make_move_iterator(other.m_terms.end())); + std::make_move_iterator(other.ops.begin()), + std::make_move_iterator(other.ops.end())); return product_operator(combined_terms); } template -product_operator product_operator::operator*=(product_operator other) { +product_operator product_operator::operator*=(const product_operator other) { *this = *this * other; return *this; } template -operator_sum product_operator::operator+(elementary_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator+(const HandlerTy other) const { + std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum({*this, product_operator(_other)}); } template -operator_sum product_operator::operator-(elementary_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator-(const HandlerTy other) const { + std::vector> _other = { other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, -1. * product_operator(_other)}); } template -product_operator product_operator::operator*(elementary_operator other) { - std::vector>> - combined_terms = m_terms; +product_operator product_operator::operator*(const HandlerTy other) const { + std::vector> + combined_terms = ops; combined_terms.push_back(other); return product_operator(combined_terms); } template -product_operator product_operator::operator*=(elementary_operator other) { +product_operator product_operator::operator*=(const HandlerTy other) { *this = *this * other; return *this; } template -operator_sum product_operator::operator+(operator_sum other) { +operator_sum product_operator::operator+(const operator_sum other) const { std::vector other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); + return operator_sum(other_terms); } template -operator_sum product_operator::operator-(operator_sum other) { +operator_sum product_operator::operator-(const operator_sum other) const { auto negative_other = (-1. * other); std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); + return operator_sum(other_terms); } template -operator_sum product_operator::operator*(operator_sum other) { +operator_sum product_operator::operator*(const operator_sum other) const { std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) { term = *this * term; } - return operator_sum(other_terms); + return operator_sum(other_terms); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index d8a68bb86b..a31a1257e5 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,12 +14,6 @@ namespace cudaq { -/// Constructors. -scalar_operator::scalar_operator(const scalar_operator &other) - : generator(other.generator), m_constant_value(other.m_constant_value) {} -scalar_operator::scalar_operator(scalar_operator &other) - : generator(other.generator), m_constant_value(other.m_constant_value) {} - /// @brief Constructor that just takes and returns a complex double value. scalar_operator::scalar_operator(std::complex value) { m_constant_value = value; @@ -40,7 +34,7 @@ scalar_operator::scalar_operator(double value) { } std::complex scalar_operator::evaluate( - std::map> parameters) const { + const std::map> parameters) const { return generator(parameters); } @@ -52,182 +46,146 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator operator op(const scalar_operator &self, \ + const scalar_operator other) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return self \ + .evaluate(parameters) op other \ + .evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_SCALAR_OPS(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS(-); +ARITHMETIC_OPERATIONS_SCALAR_OPS(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS(/); + #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator operator op(std::complex other, \ - scalar_operator self) { \ - /* Create an operator for the complex double value. */ \ - auto otherOperator = scalar_operator(other); \ - /* Create an operator that we will store the result in and return to the \ - * user. */ \ - scalar_operator returnOperator; \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - returnOperator._operators_to_compose.push_back(self); \ - returnOperator._operators_to_compose.push_back(otherOperator); \ + scalar_operator operator op(const scalar_operator &self, \ + const std::complex other) { \ auto newGenerator = \ [&](std::map> parameters) { \ - /* FIXME: I have to use this hacky `.get_val()` on the newly created \ - * operator for the given complex double -- because calling the \ - * evaluate function returns 0.0 . I have no clue why??? */ \ - return returnOperator._operators_to_compose[0] \ - .evaluate(parameters) op returnOperator._operators_to_compose[1] \ - .get_val(); \ + return self.evaluate(parameters) op other; \ }; \ - returnOperator.generator = ScalarCallbackFunction(newGenerator); \ - return returnOperator; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(scalar_operator self, \ - std::complex other) { \ - /* Create an operator for the complex double value. */ \ - auto otherOperator = scalar_operator(other); \ - /* Create an operator that we will store the result in and return to the \ - * user. */ \ - scalar_operator returnOperator; \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - returnOperator._operators_to_compose.push_back(self); \ - returnOperator._operators_to_compose.push_back(otherOperator); \ +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); + +#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ + scalar_operator operator op(const scalar_operator &self, double other) { \ auto newGenerator = \ [&](std::map> parameters) { \ - /* FIXME: I have to use this hacky `.get_val()` on the newly created \ - * operator for the given complex double -- because calling the \ - * evaluate function returns 0.0 . I have no clue why??? */ \ - return returnOperator._operators_to_compose[1] \ - .get_val() op returnOperator._operators_to_compose[0] \ + return self \ + .evaluate(parameters) op other; \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_DOUBLES(+); +ARITHMETIC_OPERATIONS_DOUBLES(-); +ARITHMETIC_OPERATIONS_DOUBLES(*); +ARITHMETIC_OPERATIONS_DOUBLES(/); + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, const scalar_operator other) { \ + /* Need to move the existing generating function to a new operator so \ + * that we can modify the generator in `self` in-place. */ \ + scalar_operator prevSelf(self); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return prevSelf \ + .evaluate(parameters) op other \ .evaluate(parameters); \ }; \ - returnOperator.generator = ScalarCallbackFunction(newGenerator); \ - return returnOperator; \ + self.generator = ScalarCallbackFunction(newGenerator); \ } +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); + #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, std::complex other) { \ - /* Create an operator for the complex double value. */ \ - auto otherOperator = scalar_operator(other); \ + void operator op(scalar_operator &self, const std::complex other) { \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in `self` in-place. */ \ - scalar_operator copy(self); \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - self._operators_to_compose.push_back(copy); \ - self._operators_to_compose.push_back(otherOperator); \ + scalar_operator prevSelf(self); \ auto newGenerator = \ [&](std::map> parameters) { \ - /* FIXME: I have to use this hacky `.get_val()` on the newly created \ - * operator for the given complex double -- because calling the \ - * evaluate function returns 0.0 . I have no clue why??? */ \ - return self._operators_to_compose[0] \ - .evaluate(parameters) op self._operators_to_compose[1] \ - .get_val(); \ + return prevSelf \ + .evaluate(parameters) op other; \ }; \ self.generator = ScalarCallbackFunction(newGenerator); \ } -#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ - scalar_operator operator op(double other, scalar_operator self) { \ - std::complex value(other, 0.0); \ - return self op value; \ - } - -#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(scalar_operator self, double other) { \ - std::complex value(other, 0.0); \ - return value op self; \ - } +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); #define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ void operator op(scalar_operator &self, double other) { \ - std::complex value(other, 0.0); \ - self op value; \ + /* Need to move the existing generating function to a new operator so that \ + * we can modify the generator in `self` in-place. */ \ + scalar_operator prevSelf(self); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return prevSelf \ + .evaluate(parameters) op other; \ + }; \ + self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_DOUBLES(+); -ARITHMETIC_OPERATIONS_DOUBLES(-); -ARITHMETIC_OPERATIONS_DOUBLES(*); -ARITHMETIC_OPERATIONS_DOUBLES(/); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op(scalar_operator other) { \ - /* Create an operator that we will store the result in and return to the \ - * user. */ \ - scalar_operator returnOperator; \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - returnOperator._operators_to_compose.push_back(*this); \ - returnOperator._operators_to_compose.push_back(other); \ +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(const std::complex other, \ + const scalar_operator self) { \ auto newGenerator = \ [&](std::map> parameters) { \ - return returnOperator._operators_to_compose[0] \ - .evaluate(parameters) op returnOperator._operators_to_compose[1] \ - .evaluate(parameters); \ + return other op self.evaluate(parameters); \ }; \ - returnOperator.generator = ScalarCallbackFunction(newGenerator); \ - return returnOperator; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, scalar_operator other) { \ - /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in `self` in-place. */ \ - scalar_operator selfCopy(self); \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - self._operators_to_compose.push_back(selfCopy); \ - self._operators_to_compose.push_back(other); \ +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); + +#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ + scalar_operator operator op(double other, const scalar_operator self) { \ auto newGenerator = \ [&](std::map> parameters) { \ - return self._operators_to_compose[0] \ - .evaluate(parameters) op self._operators_to_compose[1] \ - .evaluate(parameters); \ + return other op self.evaluate(parameters); \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_SCALAR_OPS(+); -ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -ARITHMETIC_OPERATIONS_SCALAR_OPS(*); -ARITHMETIC_OPERATIONS_SCALAR_OPS(/); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); template -operator_sum scalar_operator::operator+(elementary_operator other) { +operator_sum scalar_operator::operator+(const HandlerTy other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum({product_operator({*this}), product_operator({other})}); } template -operator_sum scalar_operator::operator-(elementary_operator other) { +operator_sum scalar_operator::operator-(const HandlerTy other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum( @@ -235,23 +193,23 @@ operator_sum scalar_operator::operator-(elementary_operator -product_operator scalar_operator::operator*(elementary_operator other) { +product_operator scalar_operator::operator*(const HandlerTy other) const { return product_operator({*this, other}); } template -operator_sum scalar_operator::operator+(product_operator other) { +operator_sum scalar_operator::operator+(const product_operator other) const { return operator_sum({product_operator({*this}), other}); } template -operator_sum scalar_operator::operator-(product_operator other) { +operator_sum scalar_operator::operator-(const product_operator other) const { return operator_sum({product_operator({*this}), (-1. * other)}); } template -product_operator scalar_operator::operator*(product_operator other) { - std::vector>> other_terms = +product_operator scalar_operator::operator*(const product_operator other) const { + std::vector> other_terms = other.get_terms(); /// Insert this scalar operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); @@ -259,14 +217,14 @@ product_operator scalar_operator::operator*(product_operator -operator_sum scalar_operator::operator+(operator_sum other) { +operator_sum scalar_operator::operator+(const operator_sum other) const { std::vector> other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } template -operator_sum scalar_operator::operator-(operator_sum other) { +operator_sum scalar_operator::operator-(const operator_sum other) const { auto negative_other = (-1. * other); std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); @@ -274,7 +232,7 @@ operator_sum scalar_operator::operator-(operator_sum other } template -operator_sum scalar_operator::operator*(operator_sum other) { +operator_sum scalar_operator::operator*(const operator_sum other) const { std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) term = *this * term; diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index f5e4dca422..c2767b6af0 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -24,7 +24,6 @@ class operator_sum; template class product_operator; -template class elementary_operator; class scalar_operator; @@ -37,24 +36,23 @@ template // handler needs to inherit from operation_handler class operator_sum { private: - std::vector> m_terms; + std::vector> terms; - std::vector>> + std::vector> canonicalize_product(product_operator &prod) const; - std::vector>> + std::vector> _canonical_terms() const; public: - /// @brief Empty constructor that a user can aggregate terms into. - // operator_sum() = default; // FIXME: NEEDED? - /// @brief Construct a `cudaq::operator_sum` given a sequence of /// `cudaq::product_operator`'s. /// This operator expression represents a sum of terms, where each term /// is a product of elementary and scalar operators. operator_sum(const std::vector> &terms) - : m_terms(terms) {} + : terms(terms) {} + + ~operator_sum() = default; operator_sum canonicalize() const; @@ -82,8 +80,8 @@ class operator_sum { // Arithmetic operators operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(operator_sum &other) const; - operator_sum operator*=(operator_sum &other); + operator_sum operator*(const operator_sum &other) const; + operator_sum operator*=(const operator_sum &other); operator_sum operator+=(const operator_sum &other); operator_sum operator-=(const operator_sum &other); operator_sum operator*(const scalar_operator &other) const; @@ -92,12 +90,12 @@ class operator_sum { operator_sum operator*=(const scalar_operator &other); operator_sum operator+=(const scalar_operator &other); operator_sum operator-=(const scalar_operator &other); - operator_sum operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - operator_sum operator*=(std::complex other); - operator_sum operator+=(std::complex other); - operator_sum operator-=(std::complex other); + operator_sum operator*(const std::complex other) const; + operator_sum operator+(const std::complex other) const; + operator_sum operator-(const std::complex other) const; + operator_sum operator*=(const std::complex other); + operator_sum operator+=(const std::complex other); + operator_sum operator-=(const std::complex other); operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; @@ -110,18 +108,18 @@ class operator_sum { operator_sum operator*=(const product_operator &other); operator_sum operator+=(const product_operator &other); operator_sum operator-=(const product_operator &other); - operator_sum operator+(const elementary_operator &other) const; - operator_sum operator-(const elementary_operator &other) const; - operator_sum operator*(const elementary_operator &other) const; - operator_sum operator*=(const elementary_operator &other); - operator_sum operator+=(const elementary_operator &other); - operator_sum operator-=(const elementary_operator &other); + operator_sum operator+(const HandlerTy &other) const; + operator_sum operator-(const HandlerTy &other) const; + operator_sum operator*(const HandlerTy &other) const; + operator_sum operator*=(const HandlerTy &other); + operator_sum operator+=(const HandlerTy &other); + operator_sum operator-=(const HandlerTy &other); /// @brief Return the operator_sum as a string. std::string to_string() const; /// @brief Return the number of operator terms that make up this operator sum. - int term_count() const { return m_terms.size(); } + int term_count() const { return terms.size(); } /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator @@ -136,21 +134,21 @@ class operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() { return m_terms; } + std::vector> get_terms() const { return terms; } }; template -operator_sum operator*(std::complex other, operator_sum self); +operator_sum operator*(const std::complex other, const operator_sum self); template -operator_sum operator+(std::complex other, operator_sum self); +operator_sum operator+(const std::complex other, const operator_sum self); template -operator_sum operator-(std::complex other, operator_sum self); +operator_sum operator-(const std::complex other, const operator_sum self); template -operator_sum operator*(double other, operator_sum self); +operator_sum operator*(double other, const operator_sum self); template -operator_sum operator+(double other, operator_sum self); +operator_sum operator+(double other, const operator_sum self); template -operator_sum operator-(double other, operator_sum self); +operator_sum operator-(double other, const operator_sum self); /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within @@ -160,45 +158,44 @@ template // handler needs to inherit from operation_handler class product_operator : public operator_sum { private: - std::vector>> m_terms; + std::vector> ops; public: - // product_operator() = default; // FIXME: needed? - ~product_operator() = default; - // Constructor for an operator expression that represents a product // of scalar and elementary operators. // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. product_operator( - std::vector>> + std::vector> atomic_operators) - : m_terms(atomic_operators) {} + : operator_sum({*this}), ops(atomic_operators) {} + + ~product_operator() = default; // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - product_operator operator*=(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); + operator_sum operator+(const std::complex other) const; + operator_sum operator-(const std::complex other) const; + product_operator operator*(const std::complex other) const; + product_operator operator*=(const std::complex other); + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*(double other) const; product_operator operator*=(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); - product_operator operator*=(scalar_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - product_operator operator*=(product_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - product_operator operator*=(elementary_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator*(operator_sum other); + operator_sum operator+(const scalar_operator other) const; + operator_sum operator-(const scalar_operator other) const; + product_operator operator*(const scalar_operator other) const; + product_operator operator*=(const scalar_operator other); + operator_sum operator+(const product_operator other) const; + operator_sum operator-(const product_operator other) const; + product_operator operator*(const product_operator other) const; + product_operator operator*=(const product_operator other); + operator_sum operator+(const HandlerTy other) const; + operator_sum operator-(const HandlerTy other) const; + product_operator operator*(const HandlerTy other) const; + product_operator operator*=(const HandlerTy other); + operator_sum operator+(const operator_sum other) const; + operator_sum operator-(const operator_sum other) const; + operator_sum operator*(const operator_sum other) const; /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator @@ -235,12 +232,12 @@ class product_operator : public operator_sum { /// @brief Return the number of operator terms that make up this product /// operator. - int term_count() const { return m_terms.size(); } + int term_count() const { return ops.size(); } /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector>> get_terms() { - return m_terms; + std::vector> get_terms() const { + return ops; }; }; @@ -257,8 +254,122 @@ operator_sum operator-(double other, product_operator self template product_operator operator*(double other, product_operator self); -template -class elementary_operator : public product_operator { +// FIXME: check if we really need the inheritance from prod operator, and if so what it should be +// (check if this really should be its own class?) +// -> replace elementary operator with the operator handler, the current elem op is the handler for custom op +// -> scalar remains its own op class, but likely doesn't need to be convertible to operator (i.e. no inheritance) +class scalar_operator { + +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::complex m_constant_value; + +public: + /// @brief Constructor that just takes a callback function with no + /// arguments. + + scalar_operator(ScalarCallbackFunction &&create) { + generator = ScalarCallbackFunction(create); + } + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value); + scalar_operator(double value); + + /// NOTE: We should revisit these constructors and remove any that have + /// become unnecessary as the implementation improves. + // scalar_operator() = default; + // Copy constructor. + // scalar_operator(const scalar_operator &other); + // scalar_operator(scalar_operator &other); + + ~scalar_operator() = default; + + // Arithmetic overloads against other operator types. + scalar_operator operator+(const scalar_operator other) const; + scalar_operator operator-(const scalar_operator other) const; + scalar_operator operator*(const scalar_operator other) const; + scalar_operator operator/(const scalar_operator other) const; + scalar_operator operator+(const std::complex other) const; + scalar_operator operator-(const std::complex other) const; + scalar_operator operator*(const std::complex other) const; + scalar_operator operator/(const std::complex other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + + void operator+=(const scalar_operator other); + void operator-=(const scalar_operator other); + void operator*=(const scalar_operator other); + void operator/=(const scalar_operator other); + void operator+=(const std::complex other); + void operator-=(const std::complex other); + void operator*=(const std::complex other); + void operator/=(const std::complex other); + void operator+=(double other); + void operator-=(double other); + void operator*=(double other); + void operator/=(double other); + + /// TODO: implement and test pow + scalar_operator pow(const scalar_operator other) const; + template + operator_sum operator+(const HandlerTy other) const; + template + operator_sum operator-(const HandlerTy other) const; + template + product_operator operator*(const HandlerTy other) const; + template + operator_sum operator+(const product_operator other) const; + template + operator_sum operator-(const product_operator other) const; + template + product_operator operator*(const product_operator other) const; + template + operator_sum operator+(const operator_sum other) const; + template + operator_sum operator-(const operator_sum other) const; + template + operator_sum operator*(const operator_sum other) const; + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(const std::map> parameters) const; + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatibility with the other inherited classes. + matrix_2 to_matrix(const std::map dimensions, + const std::map> parameters) const; + + // /// @brief Returns true if other is a scalar operator with the same + // /// generator. + // bool operator==(scalar_operator other); + + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; + + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {-1}; +}; + +scalar_operator operator+(const std::complex other, const scalar_operator self); +scalar_operator operator-(const std::complex other, const scalar_operator self); +scalar_operator operator*(const std::complex other, const scalar_operator self); +scalar_operator operator/(const std::complex other, const scalar_operator self); +scalar_operator operator+(double other, const scalar_operator self); +scalar_operator operator-(double other, const scalar_operator self); +scalar_operator operator*(double other, const scalar_operator self); +scalar_operator operator/(double other, const scalar_operator self); + +class elementary_operator : public product_operator { private: std::map m_ops; @@ -271,50 +382,53 @@ class elementary_operator : public product_operator { /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. elementary_operator(std::string operator_id, std::vector degrees) - : id(operator_id), degrees(degrees) {} + : id(operator_id), degrees(degrees), + product_operator({std::variant{*this}}) { } // Copy constructor. FIXME: NEEDED? - elementary_operator(const elementary_operator &other) + elementary_operator(const elementary_operator &other) : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} + degrees(other.degrees), id(other.id), + product_operator({std::variant{*this}}) { } - elementary_operator(elementary_operator &other) + elementary_operator(elementary_operator &other) : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} + degrees(other.degrees), id(other.id), + product_operator({std::variant{*this}}) { } + + ~elementary_operator() = default; // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); + operator_sum operator+(const std::complex other) const; + operator_sum operator-(const std::complex other) const; + product_operator operator*(const std::complex other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*(double other) const; + operator_sum operator+(scalar_operator other) const; + operator_sum operator-(scalar_operator other) const; + product_operator operator*(scalar_operator other) const; // big question regarding templates is whether coefficients are part of product or are part of elem ops - // part of product seems more intuitive to me, and probably simplifies code; // that would mean even for eager prod/add, we still get a prod/sum as result - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator+=(operator_sum other); - operator_sum operator-=(operator_sum other); - operator_sum operator*(operator_sum other); + operator_sum operator+(const elementary_operator other) const; + operator_sum operator-(const elementary_operator other) const; + product_operator operator*(const elementary_operator other) const; + operator_sum operator+(const product_operator other) const; + operator_sum operator-(const product_operator other) const; + product_operator operator*(const product_operator other) const; + operator_sum operator+(const operator_sum other) const; + operator_sum operator-(const operator_sum other) const; + operator_sum operator*(const operator_sum other) const; /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + bool operator==(elementary_operator other); - /// @brief Return the `elementary_operator` as a string. + /// @brief Return the `elementary_operator` as a string. std::string to_string() const; - /// @brief Return the `elementary_operator` as a matrix. + /// @brief Return the `elementary_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -324,18 +438,18 @@ class elementary_operator : public product_operator { const std::map> parameters) const; // Predefined operators. - static elementary_operator identity(int degree); - static elementary_operator zero(int degree); - static elementary_operator annihilate(int degree); - static elementary_operator create(int degree); - static elementary_operator momentum(int degree); - static elementary_operator number(int degree); - static elementary_operator parity(int degree); - static elementary_operator position(int degree); + static elementary_operator identity(int degree); + static elementary_operator zero(int degree); + static elementary_operator annihilate(int degree); + static elementary_operator create(int degree); + static elementary_operator momentum(int degree); + static elementary_operator number(int degree); + static elementary_operator parity(int degree); + static elementary_operator position(int degree); /// FIXME: - static elementary_operator squeeze(int degree, + static elementary_operator squeeze(int degree, std::complex amplitude); - static elementary_operator displace(int degree, + static elementary_operator displace(int degree, std::complex amplitude); /// @brief Adds the definition of an elementary operator with the given id to @@ -387,139 +501,22 @@ class elementary_operator : public product_operator { /// order. std::vector degrees; std::string id; - - // /// @brief Creates a representation of the operator as `pauli_word` that - // can be passed as an argument to quantum kernels. - // pauli_word to_pauli_word ovveride(); }; + // Reverse order arithmetic for elementary operators against pure scalars. template -operator_sum operator+(std::complex other, elementary_operator self); +operator_sum operator+(const std::complex other, const elementary_operator self); template -operator_sum operator-(std::complex other, elementary_operator self); +operator_sum operator-(const std::complex other, const elementary_operator self); template -product_operator operator*(std::complex other, - elementary_operator self); +product_operator operator*(const std::complex other, + const elementary_operator self); template -operator_sum operator+(double other, elementary_operator self); +operator_sum operator+(double other, const elementary_operator self); template -operator_sum operator-(double other, elementary_operator self); +operator_sum operator-(double other, const elementary_operator self); template -product_operator operator*(double other, elementary_operator self); - -// FIXME: check if we really need the inheritance from prod operator, and if so what it should be -// (check if this really should be its own class?) -class scalar_operator { - -private: - // If someone gave us a constant value, we will just return that - // directly to them when they call `evaluate`. - std::complex m_constant_value; - -public: - /// @brief Constructor that just takes a callback function with no - /// arguments. - - scalar_operator(ScalarCallbackFunction &&create) { - generator = ScalarCallbackFunction(create); - } - - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value); - scalar_operator(double value); - - // Arithmetic overloads against other operator types. - scalar_operator operator+(scalar_operator other); - scalar_operator operator-(scalar_operator other); - scalar_operator operator*(scalar_operator other); - scalar_operator operator/(scalar_operator other); - /// TODO: implement and test pow - scalar_operator pow(scalar_operator other); - template - operator_sum operator+(elementary_operator other); - template - operator_sum operator-(elementary_operator other); - template - product_operator operator*(elementary_operator other); - template - operator_sum operator+(product_operator other); - template - operator_sum operator-(product_operator other); - template - product_operator operator*(product_operator other); - template - operator_sum operator+(operator_sum other); - template - operator_sum operator-(operator_sum other); - template - operator_sum operator*(operator_sum other); - - /// @brief Return the scalar operator as a concrete complex value. - std::complex - evaluate(std::map> parameters) const; - - // Return the scalar operator as a 1x1 matrix. This is needed for - // compatibility with the other inherited classes. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); - - // /// @brief Returns true if other is a scalar operator with the same - // /// generator. - // bool operator==(scalar_operator other); - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - - // Only populated when we've performed arithmetic between various - // scalar operators. - std::vector _operators_to_compose; - - /// NOTE: We should revisit these constructors and remove any that have - /// become unnecessary as the implementation improves. - scalar_operator() = default; - // Copy constructor. - scalar_operator(const scalar_operator &other); - scalar_operator(scalar_operator &other); - - ~scalar_operator() = default; - - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {-1}; - - // REMOVEME: just using this as a temporary patch: - std::complex get_val() { return m_constant_value; }; -}; - -scalar_operator operator+(scalar_operator self, std::complex other); -scalar_operator operator-(scalar_operator self, std::complex other); -scalar_operator operator*(scalar_operator self, std::complex other); -scalar_operator operator/(scalar_operator self, std::complex other); -scalar_operator operator+(std::complex other, scalar_operator self); -scalar_operator operator-(std::complex other, scalar_operator self); -scalar_operator operator*(std::complex other, scalar_operator self); -scalar_operator operator/(std::complex other, scalar_operator self); -scalar_operator operator+(scalar_operator self, double other); -scalar_operator operator-(scalar_operator self, double other); -scalar_operator operator*(scalar_operator self, double other); -scalar_operator operator/(scalar_operator self, double other); -scalar_operator operator+(double other, scalar_operator self); -scalar_operator operator-(double other, scalar_operator self); -scalar_operator operator*(double other, scalar_operator self); -scalar_operator operator/(double other, scalar_operator self); -void operator+=(scalar_operator &self, std::complex other); -void operator-=(scalar_operator &self, std::complex other); -void operator*=(scalar_operator &self, std::complex other); -void operator/=(scalar_operator &self, std::complex other); -void operator+=(scalar_operator &self, scalar_operator other); -void operator-=(scalar_operator &self, scalar_operator other); -void operator*=(scalar_operator &self, scalar_operator other); -void operator/=(scalar_operator &self, scalar_operator other); +product_operator operator*(double other, const elementary_operator self); /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian : public operator_sum { From 3511e7c101252b5becdd7253c6e6c1377f9b3627 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 19:37:02 +0000 Subject: [PATCH 049/311] wip to use elementary_operator as matrix handler Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 175 ++++---- runtime/cudaq/dynamics/operator_sum.cpp | 316 ++++++++------ runtime/cudaq/dynamics/product_operators.cpp | 214 ++++++---- runtime/cudaq/dynamics/scalar_operators.cpp | 206 ++++------ runtime/cudaq/operators.h | 388 +++++++++--------- 5 files changed, 650 insertions(+), 649 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index baa5df8396..a13d0d1aa2 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -269,85 +269,116 @@ elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } +// evaluations + matrix_2 elementary_operator::to_matrix( std::map dimensions, std::map> parameters) { return m_ops[id].generator(dimensions, parameters); } -/// Elementary Operator Arithmetic. +// right-hand arithmetics + +product_operator elementary_operator::operator*(double other) const { + std::complex value(other, 0.0); + return *this * value; +} + +operator_sum elementary_operator::operator+(double other) const { + std::complex value(other, 0.0); + return *this + value; +} + +operator_sum elementary_operator::operator-(double other) const { + std::complex value(other, 0.0); + return *this - value; +} + +product_operator elementary_operator::operator*(std::complex other) const { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + *this, other_scalar}; + return product_operator(_args); +} -operator_sum elementary_operator::operator+(const scalar_operator other) const { +operator_sum elementary_operator::operator+(std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator(other); std::vector> _this = { *this}; std::vector> _other = { - other}; + other_scalar}; return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(const scalar_operator other) const { +operator_sum elementary_operator::operator-(std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator((-1. * other)); std::vector> _this = { *this}; std::vector> _other = { - -1. * other}; + other_scalar}; return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(const scalar_operator other) const { +product_operator elementary_operator::operator*(const scalar_operator &other) const { std::vector> _args = { *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(const std::complex other) const { +operator_sum elementary_operator::operator+(const scalar_operator &other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator(other); std::vector> _this = { *this}; std::vector> _other = { - other_scalar}; + other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(const std::complex other) const { +operator_sum elementary_operator::operator-(const scalar_operator &other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator((-1. * other)); std::vector> _this = { *this}; std::vector> _other = { - other_scalar}; + -1. * other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(const std::complex other) const { - auto other_scalar = scalar_operator(other); +product_operator elementary_operator::operator*(const elementary_operator &other) const { std::vector> _args = { - *this, other_scalar}; + *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(double other) const { - std::complex value(other, 0.0); - return *this + value; +operator_sum elementary_operator::operator+(const elementary_operator &other) const { + std::vector> _this = { + *this}; + std::vector> _other = { + other}; + return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(double other) const { - std::complex value(other, 0.0); - return *this - value; +operator_sum elementary_operator::operator-(const elementary_operator &other) const { + std::vector> _this = { + *this}; + return operator_sum({product_operator(_this), (-1. * other)}); } -product_operator elementary_operator::operator*(double other) const { - std::complex value(other, 0.0); - return *this * value; +// left-hand arithmetics + +product_operator operator*(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); } -operator_sum operator+(const std::complex other, const elementary_operator self) { +operator_sum operator+(double other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _self = { self}; @@ -356,109 +387,51 @@ operator_sum operator+(const std::complex other, co return operator_sum({product_operator(_other), product_operator(_self)}); } -template -operator_sum operator-(const std::complex other, const elementary_operator self) { +operator_sum operator-(double other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); + return operator_sum({product_operator(_other), (-1. * self)}); } -template -product_operator operator*(const std::complex other, - const elementary_operator self) { +product_operator operator*(std::complex other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _args = { other_scalar, self}; return product_operator(_args); } -template -operator_sum operator+(double other, const elementary_operator self) { +operator_sum operator+(std::complex other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _self = { self}; std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum({product_operator(_other), product_operator(_self)}); } -template -operator_sum operator-(double other, const elementary_operator self) { +operator_sum operator-(std::complex other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); -} - -template -product_operator operator*(double other, const elementary_operator self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); -} - -product_operator elementary_operator::operator*(const elementary_operator other) const { - std::vector> _args = { - *this, other}; - return product_operator(_args); -} - -operator_sum elementary_operator::operator+(const elementary_operator other) const { - std::vector> _this = { - *this}; - std::vector> _other = { - other}; - return operator_sum({product_operator(_this), product_operator(_other)}); -} - -operator_sum elementary_operator::operator-(const elementary_operator other) const { - std::vector> _this = { - *this}; - return operator_sum({product_operator(_this), (-1. * other)}); + return operator_sum({product_operator(_other), (-1. * self)}); } -operator_sum elementary_operator::operator+(const operator_sum other) const { - std::vector> _this = { - *this}; - std::vector> _prods = {product_operator(_this)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + other; -} - -operator_sum elementary_operator::operator-(const operator_sum other) const { - std::vector> _this = { - *this}; - std::vector> _prods = {product_operator(_this)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - other; +product_operator operator*(const scalar_operator &other, const elementary_operator &self) { + return product_operator({other, self}); } -operator_sum elementary_operator::operator*(const operator_sum other) const { - std::vector> _this = { - *this}; - std::vector> _prods = {product_operator(_this)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * other; -} - -operator_sum elementary_operator::operator+(const product_operator other) const { - std::vector> _this = { - *this}; - return operator_sum({product_operator(_this), other}); -} - -operator_sum elementary_operator::operator-(const product_operator other) const { - return *this + (-1. * other); +operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum({product_operator({other}), product_operator({self})}); } -product_operator elementary_operator::operator*(const product_operator other) const { - std::vector> other_terms = - other.get_terms(); - /// Insert this elementary operator to the front of the terms list. - other_terms.insert(other_terms.begin(), *this); - return product_operator(other_terms); +operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum( + {product_operator({other}), product_operator({-1. * self})}); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index e3a3e97e45..e2c238e932 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -118,53 +118,91 @@ namespace cudaq { template class operator_sum; -// Arithmetic operators +// evaluations + +/// FIXME: +// tensor +// operator_sum::to_matrix(const std::map &dimensions, +// const std::map ¶ms) const { +// // todo +// } + +// std::string operator_sum::to_string() const { +// std::string result; +// // for (const auto &term : terms) { +// // result += term.to_string() + " + "; +// // } +// // // Remove last " + " +// // if (!result.empty()) +// // result.pop_back(); +// return result; +// } + +// right-hand arithmetics + template -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = terms; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.terms.begin()), - std::make_move_iterator(other.terms.end())); - return operator_sum(combined_terms); +operator_sum operator_sum::operator*(double other) const { + return *this * scalar_operator(other); } template -operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (-1 * other); +operator_sum operator_sum::operator+(double other) const { + return *this + scalar_operator(other); } -//template -//operator_sum operator_sum::operator-( -// const operator_sum &other) const; +template +operator_sum operator_sum::operator-(double other) const { + return *this - scalar_operator(other); +} template -operator_sum operator_sum::operator-=(const operator_sum &other) { - *this = *this - other; +operator_sum operator_sum::operator*=(double other) { + *this *= scalar_operator(other); return *this; } template -operator_sum operator_sum::operator+=(const operator_sum &other) { - *this = *this + other; +operator_sum operator_sum::operator+=(double other) { + *this += scalar_operator(other); return *this; } template -operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = terms; - std::vector> product_terms; - auto other_terms = other.get_terms(); - for (auto &term : self_terms) { - for (auto &other_term : other_terms) { - product_terms.push_back(term * other_term); - } - } - return operator_sum(product_terms); +operator_sum operator_sum::operator-=(double other) { + *this -= scalar_operator(other); + return *this; } template -operator_sum operator_sum::operator*=(const operator_sum &other) { - *this = *this * other; +operator_sum operator_sum::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +operator_sum operator_sum::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +template +operator_sum operator_sum::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +template +operator_sum operator_sum::operator*=(std::complex other) { + *this *= scalar_operator(other); + return *this; +} + +template +operator_sum operator_sum::operator+=(std::complex other) { + *this += scalar_operator(other); + return *this; +} + +template +operator_sum operator_sum::operator-=(std::complex other) { + *this -= scalar_operator(other); return *this; } @@ -210,203 +248,217 @@ operator_sum operator_sum::operator-=(const scalar_operato } template -operator_sum operator_sum::operator*(std::complex other) const { - return *this * scalar_operator(other); +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator+(std::complex other) const { - return *this + scalar_operator(other); +operator_sum operator_sum::operator+(const HandlerTy &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator-(std::complex other) const { - return *this - scalar_operator(other); +operator_sum operator_sum::operator-(const HandlerTy &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back((-1. * other)); + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator*=(std::complex other) { - *this *= scalar_operator(other); +operator_sum operator_sum::operator*=(const HandlerTy &other) { + *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(std::complex other) { - *this += scalar_operator(other); +operator_sum operator_sum::operator+=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this + product_operator(_other); return *this; } template -operator_sum operator_sum::operator-=(std::complex other) { - *this -= scalar_operator(other); +operator_sum operator_sum::operator-=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this - product_operator(_other); return *this; } template -operator_sum operator_sum::operator*(double other) const { - return *this * scalar_operator(other); +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator+(double other) const { - return *this + scalar_operator(other); +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other); + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator-(double other) const { - return *this - scalar_operator(other); +operator_sum operator_sum::operator-(const product_operator &other) const { + return *this + (-1. * other); } template -operator_sum operator_sum::operator*=(double other) { - *this *= scalar_operator(other); +operator_sum operator_sum::operator*=(const product_operator &other) { + *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(double other) { - *this += scalar_operator(other); +operator_sum operator_sum::operator+=(const product_operator &other) { + *this = *this + other; return *this; } template -operator_sum operator_sum::operator-=(double other) { - *this -= scalar_operator(other); +operator_sum operator_sum::operator-=(const product_operator &other) { + *this = *this - other; return *this; } template -operator_sum operator*(std::complex other, operator_sum self) { - return scalar_operator(other) * self; +operator_sum operator_sum::operator*(const operator_sum &other) const { + auto self_terms = terms; + std::vector> product_terms; + auto other_terms = other.get_terms(); + for (auto &term : self_terms) { + for (auto &other_term : other_terms) { + product_terms.push_back(term * other_term); + } + } + return operator_sum(product_terms); } template -operator_sum operator+(std::complex other, operator_sum self) { - return scalar_operator(other) + self; +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector> combined_terms = terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.terms.begin()), + std::make_move_iterator(other.terms.end())); + return operator_sum(combined_terms); } template -operator_sum operator-(std::complex other, operator_sum self) { - return scalar_operator(other) - self; +operator_sum operator_sum::operator-(const operator_sum &other) const { + return *this + (-1 * other); } +//template +//operator_sum operator_sum::operator-( +// const operator_sum &other) const; + template -operator_sum operator*(double other, operator_sum self) { - return scalar_operator(other) * self; +operator_sum operator_sum::operator*=(const operator_sum &other) { + *this = *this * other; + return *this; } template -operator_sum operator+(double other, operator_sum self) { - return scalar_operator(other) + self; +operator_sum operator_sum::operator-=(const operator_sum &other) { + *this = *this - other; + return *this; } template -operator_sum operator-(double other, operator_sum self) { - return scalar_operator(other) - self; +operator_sum operator_sum::operator+=(const operator_sum &other) { + *this = *this + other; + return *this; } +// left-hand arithmetics + template -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back(other); - return operator_sum(combined_terms); +operator_sum operator*(double other, const operator_sum &self) { + return scalar_operator(other) * self; } template -operator_sum operator_sum::operator+=(const product_operator &other) { - *this = *this + other; - return *this; +operator_sum operator+(double other, const operator_sum &self) { + return scalar_operator(other) + self; } template -operator_sum operator_sum::operator-(const product_operator &other) const { - return *this + (-1. * other); +operator_sum operator-(double other, const operator_sum &self) { + return scalar_operator(other) - self; } template -operator_sum operator_sum::operator-=(const product_operator &other) { - *this = *this - other; - return *this; +operator_sum operator*(std::complex other, const operator_sum &self) { + return scalar_operator(other) * self; } template -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); +operator_sum operator+(std::complex other, const operator_sum &self) { + return scalar_operator(other) + self; } template -operator_sum operator_sum::operator*=(const product_operator &other) { - *this = *this * other; - return *this; +operator_sum operator-(std::complex other, const operator_sum &self) { + return scalar_operator(other) - self; } template -operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); - return operator_sum(combined_terms); +operator_sum operator*(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + for (auto &term : terms) + term = other * term; + return operator_sum(terms); } template -operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back((-1. * other)); - return operator_sum(combined_terms); +operator_sum operator+(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); } template -operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); +operator_sum operator-(const scalar_operator &other, const operator_sum &self) { + auto negative_self = (-1. * self); + std::vector> terms = negative_self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); } template -operator_sum operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this + product_operator(_other); - return *this; +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * self; } template -operator_sum operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this - product_operator(_other); - return *this; +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + self; } template -operator_sum operator_sum::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - self; } -/// FIXME: -// tensor -// operator_sum::to_matrix(const std::map &dimensions, -// const std::map ¶ms) const { -// // todo -// } - -// std::string operator_sum::to_string() const { -// std::string result; -// // for (const auto &term : terms) { -// // result += term.to_string() + " + "; -// // } -// // // Remove last " + " -// // if (!result.empty()) -// // result.pop_back(); -// return result; -// } - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 293fb2cfc1..8db05cd0da 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -163,182 +163,226 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } + +// right-hand arithmetics + template -operator_sum product_operator::operator+(const scalar_operator other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); +product_operator product_operator::operator*(double other) const { + return *this * scalar_operator(other); } template -operator_sum product_operator::operator-(const scalar_operator other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); +operator_sum product_operator::operator+(double other) const { + return *this + scalar_operator(other); } template -product_operator product_operator::operator*(const scalar_operator other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); +operator_sum product_operator::operator-(double other) const { + return *this - scalar_operator(other); } template -product_operator product_operator::operator*=(const scalar_operator other) { - *this = *this * other; +product_operator product_operator::operator*=(double other) { + *this = *this * scalar_operator(other); return *this; } template -operator_sum product_operator::operator+(const std::complex other) const { +product_operator product_operator::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +operator_sum product_operator::operator+(std::complex other) const { return *this + scalar_operator(other); } template -operator_sum product_operator::operator-(const std::complex other) const { +operator_sum product_operator::operator-(std::complex other) const { return *this - scalar_operator(other); } template -product_operator product_operator::operator*(const std::complex other) const { - return *this * scalar_operator(other); +product_operator product_operator::operator*=(std::complex other) { + *this = *this * scalar_operator(other); + return *this; } template -product_operator product_operator::operator*=(const std::complex other) { - *this = *this * scalar_operator(other); +product_operator product_operator::operator*(const scalar_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +template +operator_sum product_operator::operator+(const scalar_operator &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +template +operator_sum product_operator::operator-(const scalar_operator &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); +} + +template +product_operator product_operator::operator*=(const scalar_operator &other) { + *this = *this * other; return *this; } template -operator_sum operator+(const std::complex other, const product_operator self) { - return operator_sum({scalar_operator(other), self}); +product_operator product_operator::operator*(const HandlerTy &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); } template -operator_sum operator-(const std::complex other, const product_operator self) { - return scalar_operator(other) - self; +operator_sum product_operator::operator+(const HandlerTy &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); } template -product_operator operator*(const std::complex other, const product_operator self) { - return scalar_operator(other) * self; +operator_sum product_operator::operator-(const HandlerTy &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); } template -operator_sum product_operator::operator+(double other) const { - return *this + scalar_operator(other); +product_operator product_operator::operator*=(const HandlerTy &other) { + *this = *this * other; + return *this; } template -operator_sum product_operator::operator-(double other) const { - return *this - scalar_operator(other); +product_operator product_operator::operator*(const product_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.ops.begin()), + std::make_move_iterator(other.ops.end())); + return product_operator(combined_terms); } template -product_operator product_operator::operator*(double other) const { - return *this * scalar_operator(other); +operator_sum product_operator::operator+(const product_operator &other) const { + return operator_sum({*this, other}); } template -product_operator product_operator::operator*=(double other) { - *this = *this * scalar_operator(other); +operator_sum product_operator::operator-(const product_operator &other) const { + return operator_sum({*this, (-1. * other)}); +} + +template +product_operator product_operator::operator*=(const product_operator &other) { + *this = *this * other; return *this; } template -operator_sum operator+(double other, const product_operator self) { - return operator_sum({scalar_operator(other), self}); +operator_sum product_operator::operator*(const operator_sum &other) const { + std::vector> other_terms = other.get_terms(); + for (auto &term : other_terms) { + term = *this * term; + } + return operator_sum(other_terms); } template -operator_sum operator-(double other, const product_operator self) { - return scalar_operator(other) - self; +operator_sum product_operator::operator+(const operator_sum &other) const { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); } template -product_operator operator*(double other, const product_operator self) { +operator_sum product_operator::operator-(const operator_sum &other) const { + auto negative_other = (-1. * other); + std::vector> other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +// left-hand arithmetics + +template +product_operator operator*(double other, const product_operator &self) { return scalar_operator(other) * self; } template -operator_sum product_operator::operator+(const product_operator other) const { - return operator_sum({*this, other}); +operator_sum operator+(double other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum product_operator::operator-(const product_operator other) const { - return operator_sum({*this, (-1. * other)}); +operator_sum operator-(double other, const product_operator &self) { + return scalar_operator(other) - self; } template -product_operator product_operator::operator*(const product_operator other) const { - std::vector> - combined_terms = ops; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.ops.begin()), - std::make_move_iterator(other.ops.end())); - return product_operator(combined_terms); +product_operator operator*(const std::complex other, const product_operator &self) { + return scalar_operator(other) * self; } template -product_operator product_operator::operator*=(const product_operator other) { - *this = *this * other; - return *this; +operator_sum operator+(const std::complex other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum product_operator::operator+(const HandlerTy other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); +operator_sum operator-(const std::complex other, const product_operator &self) { + return scalar_operator(other) - self; } template -operator_sum product_operator::operator-(const HandlerTy other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); +product_operator operator*(const scalar_operator &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this scalar operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); } template -product_operator product_operator::operator*(const HandlerTy other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); +operator_sum operator+(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); } template -product_operator product_operator::operator*=(const HandlerTy other) { - *this = *this * other; - return *this; +operator_sum operator-(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), (-1. * self)}); } template -operator_sum product_operator::operator+(const operator_sum other) const { - std::vector other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this elementary operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); } template -operator_sum product_operator::operator-(const operator_sum other) const { - auto negative_other = (-1. * other); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + std::vector> new_term = {other}; + return operator_sum({product_operator(new_term), self}); } template -operator_sum product_operator::operator*(const operator_sum other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) { - term = *this * term; - } - return operator_sum(other_terms); +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return other + (-1. * self); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index a31a1257e5..dbd1c046b7 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,6 +14,8 @@ namespace cudaq { +// constructors + /// @brief Constructor that just takes and returns a complex double value. scalar_operator::scalar_operator(std::complex value) { m_constant_value = value; @@ -33,6 +35,8 @@ scalar_operator::scalar_operator(double value) { generator = ScalarCallbackFunction(func); } +// evaluations + std::complex scalar_operator::evaluate( const std::map> parameters) const { return generator(parameters); @@ -46,37 +50,7 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const scalar_operator other) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return self \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_SCALAR_OPS(+); -ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -ARITHMETIC_OPERATIONS_SCALAR_OPS(*); -ARITHMETIC_OPERATIONS_SCALAR_OPS(/); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const std::complex other) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return self.evaluate(parameters) op other; \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); +// right-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ scalar_operator operator op(const scalar_operator &self, double other) { \ @@ -88,32 +62,46 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_DOUBLES(+); -ARITHMETIC_OPERATIONS_DOUBLES(-); ARITHMETIC_OPERATIONS_DOUBLES(*); ARITHMETIC_OPERATIONS_DOUBLES(/); +ARITHMETIC_OPERATIONS_DOUBLES(+); +ARITHMETIC_OPERATIONS_DOUBLES(-); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, const scalar_operator other) { \ - /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in `self` in-place. */ \ +#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, double other) { \ + /* Need to move the existing generating function to a new operator so that \ + * we can modify the generator in `self` in-place. */ \ scalar_operator prevSelf(self); \ auto newGenerator = \ [&](std::map> parameters) { \ return prevSelf \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ + .evaluate(parameters) op other; \ }; \ self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ + scalar_operator operator op(const scalar_operator &self, \ + const std::complex other) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return self.evaluate(parameters) op other; \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, const std::complex other) { \ + void operator op(scalar_operator &self, std::complex other) { \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in `self` in-place. */ \ scalar_operator prevSelf(self); \ @@ -125,46 +113,51 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); -#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, double other) { \ - /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator operator op(const scalar_operator &self, \ + const scalar_operator &other) { \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other; \ + return self \ + .evaluate(parameters) op other \ + .evaluate(parameters); \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_SCALAR_OPS(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(const std::complex other, \ - const scalar_operator self) { \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, const scalar_operator &other) { \ + /* Need to move the existing generating function to a new operator so \ + * that we can modify the generator in `self` in-place. */ \ + scalar_operator prevSelf(self); \ auto newGenerator = \ [&](std::map> parameters) { \ - return other op self.evaluate(parameters); \ + return prevSelf \ + .evaluate(parameters) op other \ + .evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); + +// left-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(double other, const scalar_operator self) { \ + scalar_operator operator op(double other, const scalar_operator &self) { \ auto newGenerator = \ [&](std::map> parameters) { \ return other op self.evaluate(parameters); \ @@ -172,71 +165,24 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); -template -operator_sum scalar_operator::operator+(const HandlerTy other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum({product_operator({*this}), product_operator({other})}); -} - -template -operator_sum scalar_operator::operator-(const HandlerTy other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum( - {product_operator({*this}), product_operator({-1. * other})}); -} - -template -product_operator scalar_operator::operator*(const HandlerTy other) const { - return product_operator({*this, other}); -} - -template -operator_sum scalar_operator::operator+(const product_operator other) const { - return operator_sum({product_operator({*this}), other}); -} - -template -operator_sum scalar_operator::operator-(const product_operator other) const { - return operator_sum({product_operator({*this}), (-1. * other)}); -} - -template -product_operator scalar_operator::operator*(const product_operator other) const { - std::vector> other_terms = - other.get_terms(); - /// Insert this scalar operator to the front of the terms list. - other_terms.insert(other_terms.begin(), *this); - return product_operator(other_terms); -} - -template -operator_sum scalar_operator::operator+(const operator_sum other) const { - std::vector> other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - -template -operator_sum scalar_operator::operator-(const operator_sum other) const { - auto negative_other = (-1. * other); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(std::complex other, \ + const scalar_operator &self) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return other op self.evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } -template -operator_sum scalar_operator::operator*(const operator_sum other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) - term = *this * term; - return operator_sum(other_terms); -} +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index c2767b6af0..6769e4aabb 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -78,42 +78,42 @@ class operator_sum { const std::map> ¶ms = {}) const; // Arithmetic operators - operator_sum operator+(const operator_sum &other) const; - operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(const operator_sum &other) const; - operator_sum operator*=(const operator_sum &other); - operator_sum operator+=(const operator_sum &other); - operator_sum operator-=(const operator_sum &other); - operator_sum operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*=(const scalar_operator &other); - operator_sum operator+=(const scalar_operator &other); - operator_sum operator-=(const scalar_operator &other); - operator_sum operator*(const std::complex other) const; - operator_sum operator+(const std::complex other) const; - operator_sum operator-(const std::complex other) const; - operator_sum operator*=(const std::complex other); - operator_sum operator+=(const std::complex other); - operator_sum operator-=(const std::complex other); operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; operator_sum operator*=(double other); operator_sum operator+=(double other); operator_sum operator-=(double other); - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; - operator_sum operator*=(const product_operator &other); - operator_sum operator+=(const product_operator &other); - operator_sum operator-=(const product_operator &other); + operator_sum operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + operator_sum operator*=(std::complex other); + operator_sum operator+=(std::complex other); + operator_sum operator-=(std::complex other); + operator_sum operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + operator_sum operator*=(const scalar_operator &other); + operator_sum operator+=(const scalar_operator &other); + operator_sum operator-=(const scalar_operator &other); operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; operator_sum operator*=(const HandlerTy &other); operator_sum operator+=(const HandlerTy &other); operator_sum operator-=(const HandlerTy &other); + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + operator_sum operator*=(const product_operator &other); + operator_sum operator+=(const product_operator &other); + operator_sum operator-=(const product_operator &other); + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; + operator_sum operator*(const operator_sum &other) const; + operator_sum operator*=(const operator_sum &other); + operator_sum operator+=(const operator_sum &other); + operator_sum operator-=(const operator_sum &other); /// @brief Return the operator_sum as a string. std::string to_string() const; @@ -137,18 +137,31 @@ class operator_sum { std::vector> get_terms() const { return terms; } }; +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); template -operator_sum operator*(const std::complex other, const operator_sum self); +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); template -operator_sum operator+(const std::complex other, const operator_sum self); +operator_sum operator-(std::complex other, const operator_sum &self); template -operator_sum operator-(const std::complex other, const operator_sum self); +operator_sum operator*(const scalar_operator &other, const operator_sum &self); template -operator_sum operator*(double other, const operator_sum self); +operator_sum operator+(const scalar_operator &other, const operator_sum &self); template -operator_sum operator+(double other, const operator_sum self); +operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -operator_sum operator-(double other, const operator_sum self); +operator_sum operator*(const HandlerTy &other, const operator_sum self); +template +operator_sum operator+(const HandlerTy &other, const operator_sum self); +template +operator_sum operator-(const HandlerTy &other, const operator_sum self); + /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within @@ -172,41 +185,19 @@ class product_operator : public operator_sum { ~product_operator() = default; - // Arithmetic overloads against all other operator types. - operator_sum operator+(const std::complex other) const; - operator_sum operator-(const std::complex other) const; - product_operator operator*(const std::complex other) const; - product_operator operator*=(const std::complex other); - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(double other) const; - product_operator operator*=(double other); - operator_sum operator+(const scalar_operator other) const; - operator_sum operator-(const scalar_operator other) const; - product_operator operator*(const scalar_operator other) const; - product_operator operator*=(const scalar_operator other); - operator_sum operator+(const product_operator other) const; - operator_sum operator-(const product_operator other) const; - product_operator operator*(const product_operator other) const; - product_operator operator*=(const product_operator other); - operator_sum operator+(const HandlerTy other) const; - operator_sum operator-(const HandlerTy other) const; - product_operator operator*(const HandlerTy other) const; - product_operator operator*=(const HandlerTy other); - operator_sum operator+(const operator_sum other) const; - operator_sum operator-(const operator_sum other) const; - operator_sum operator*(const operator_sum other) const; + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. - bool operator==(product_operator other); + /// @brief Return the number of operator terms that make up this product + /// operator. + int term_count() const { return ops.size(); } + + /// FIXME: Protect this once I can do deeper testing in `unittests`. + // protected: + std::vector> get_terms() const { + return ops; + }; /// @brief Return the `product_operator` as a string. std::string to_string() const; @@ -222,37 +213,68 @@ class product_operator : public operator_sum { to_matrix(const std::map dimensions, const std::map> parameters) const; - /// @brief Creates a representation of the operator as a `cudaq::pauli_word` - /// that can be passed as an argument to quantum kernels. - // pauli_word to_pauli_word(); - - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees() const; - - /// @brief Return the number of operator terms that make up this product - /// operator. - int term_count() const { return ops.size(); } + // Arithmetic overloads against all other operator types. + product_operator operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*=(double other); + product_operator operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + product_operator operator*=(std::complex other); + product_operator operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + product_operator operator*=(const scalar_operator &other); + product_operator operator*(const HandlerTy &other) const; + operator_sum operator+(const HandlerTy &other) const; + operator_sum operator-(const HandlerTy &other) const; + product_operator operator*=(const HandlerTy &other); + product_operator operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + product_operator operator*=(const product_operator &other); + operator_sum operator*(const operator_sum &other) const; + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: - std::vector> get_terms() const { - return ops; - }; + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms `blockwise`; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(product_operator other); }; template -operator_sum operator+(std::complex other, product_operator self); +product_operator operator*(double other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); template -operator_sum operator-(std::complex other, product_operator self); +operator_sum operator+(std::complex other, const product_operator &self); template -product_operator operator*(std::complex other, product_operator self); +operator_sum operator-(std::complex other, const product_operator &self); template -operator_sum operator+(double other, product_operator self); +product_operator operator*(const scalar_operator &other, const product_operator &self); template -operator_sum operator-(double other, product_operator self); +operator_sum operator+(const scalar_operator &other, const product_operator &self); template -product_operator operator*(double other, product_operator self); +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +operator_sum operator-(const HandlerTy &other, const product_operator &self); + // FIXME: check if we really need the inheritance from prod operator, and if so what it should be // (check if this really should be its own class?) @@ -288,53 +310,15 @@ class scalar_operator { ~scalar_operator() = default; - // Arithmetic overloads against other operator types. - scalar_operator operator+(const scalar_operator other) const; - scalar_operator operator-(const scalar_operator other) const; - scalar_operator operator*(const scalar_operator other) const; - scalar_operator operator/(const scalar_operator other) const; - scalar_operator operator+(const std::complex other) const; - scalar_operator operator-(const std::complex other) const; - scalar_operator operator*(const std::complex other) const; - scalar_operator operator/(const std::complex other) const; - scalar_operator operator+(double other) const; - scalar_operator operator-(double other) const; - scalar_operator operator*(double other) const; - scalar_operator operator/(double other) const; - - void operator+=(const scalar_operator other); - void operator-=(const scalar_operator other); - void operator*=(const scalar_operator other); - void operator/=(const scalar_operator other); - void operator+=(const std::complex other); - void operator-=(const std::complex other); - void operator*=(const std::complex other); - void operator/=(const std::complex other); - void operator+=(double other); - void operator-=(double other); - void operator*=(double other); - void operator/=(double other); + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; - /// TODO: implement and test pow - scalar_operator pow(const scalar_operator other) const; - template - operator_sum operator+(const HandlerTy other) const; - template - operator_sum operator-(const HandlerTy other) const; - template - product_operator operator*(const HandlerTy other) const; - template - operator_sum operator+(const product_operator other) const; - template - operator_sum operator-(const product_operator other) const; - template - product_operator operator*(const product_operator other) const; - template - operator_sum operator+(const operator_sum other) const; - template - operator_sum operator-(const operator_sum other) const; - template - operator_sum operator*(const operator_sum other) const; + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {}; /// @brief Return the scalar operator as a concrete complex value. std::complex @@ -345,29 +329,46 @@ class scalar_operator { matrix_2 to_matrix(const std::map dimensions, const std::map> parameters) const; + // Arithmetic overloads against other operator types. + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + void operator*=(double other); + void operator/=(double other); + void operator+=(double other); + void operator-=(double other); + scalar_operator operator*(std::complex other) const; + scalar_operator operator/(std::complex other) const; + scalar_operator operator+(std::complex other) const; + scalar_operator operator-(std::complex other) const; + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; + void operator*=(std::complex other); + void operator/=(std::complex other); + void operator+=(std::complex other); + void operator-=(std::complex other); + void operator*=(const scalar_operator &other); + void operator/=(const scalar_operator &other); + void operator+=(const scalar_operator &other); + void operator-=(const scalar_operator &other); + /// TODO: implement and test pow + // /// @brief Returns true if other is a scalar operator with the same // /// generator. // bool operator==(scalar_operator other); - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {-1}; }; -scalar_operator operator+(const std::complex other, const scalar_operator self); -scalar_operator operator-(const std::complex other, const scalar_operator self); -scalar_operator operator*(const std::complex other, const scalar_operator self); -scalar_operator operator/(const std::complex other, const scalar_operator self); -scalar_operator operator+(double other, const scalar_operator self); -scalar_operator operator-(double other, const scalar_operator self); -scalar_operator operator*(double other, const scalar_operator self); -scalar_operator operator/(double other, const scalar_operator self); +scalar_operator operator*(double other, const scalar_operator &self); +scalar_operator operator/(double other, const scalar_operator &self); +scalar_operator operator+(double other, const scalar_operator &self); +scalar_operator operator-(double other, const scalar_operator &self); +scalar_operator operator*(std::complex other, const scalar_operator &self); +scalar_operator operator/(std::complex other, const scalar_operator &self); +scalar_operator operator+(std::complex other, const scalar_operator &self); +scalar_operator operator-(std::complex other, const scalar_operator &self); class elementary_operator : public product_operator { @@ -398,32 +399,15 @@ class elementary_operator : public product_operator { ~elementary_operator() = default; - // Arithmetic overloads against all other operator types. - operator_sum operator+(const std::complex other) const; - operator_sum operator-(const std::complex other) const; - product_operator operator*(const std::complex other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(double other) const; - operator_sum operator+(scalar_operator other) const; - operator_sum operator-(scalar_operator other) const; - product_operator operator*(scalar_operator other) const; - // big question regarding templates is whether coefficients are part of product or are part of elem ops - - // part of product seems more intuitive to me, and probably simplifies code; - // that would mean even for eager prod/add, we still get a prod/sum as result - operator_sum operator+(const elementary_operator other) const; - operator_sum operator-(const elementary_operator other) const; - product_operator operator*(const elementary_operator other) const; - operator_sum operator+(const product_operator other) const; - operator_sum operator-(const product_operator other) const; - product_operator operator*(const product_operator other) const; - operator_sum operator+(const operator_sum other) const; - operator_sum operator-(const operator_sum other) const; - operator_sum operator*(const operator_sum other) const; - - /// @brief True, if the other value is an elementary operator with the same id - /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + /// @brief The number of levels, that is the dimension, for each degree of + /// freedom in canonical order that the operator acts on. A value of zero or + /// less indicates that the operator is defined for any dimension of that + /// degree. + std::map expected_dimensions; + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees; + std::string id; /// @brief Return the `elementary_operator` as a string. std::string to_string() const; @@ -437,6 +421,24 @@ class elementary_operator : public product_operator { to_matrix(const std::map dimensions, const std::map> parameters) const; + // Arithmetic overloads + product_operator operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + product_operator operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + product_operator operator*(const elementary_operator &other) const; + operator_sum operator+(const elementary_operator &other) const; + operator_sum operator-(const elementary_operator &other) const; + + /// @brief True, if the other value is an elementary operator with the same id + /// acting on the same degrees of freedom, and False otherwise. + bool operator==(elementary_operator other); + // Predefined operators. static elementary_operator identity(int degree); static elementary_operator zero(int degree); @@ -489,34 +491,18 @@ class elementary_operator : public product_operator { defn.create_definition(operator_id, expected_dimensions, create); m_ops[operator_id] = defn; } - - // Attributes. - - /// @brief The number of levels, that is the dimension, for each degree of - /// freedom in canonical order that the operator acts on. A value of zero or - /// less indicates that the operator is defined for any dimension of that - /// degree. - std::map expected_dimensions; - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees; - std::string id; }; // Reverse order arithmetic for elementary operators against pure scalars. -template -operator_sum operator+(const std::complex other, const elementary_operator self); -template -operator_sum operator-(const std::complex other, const elementary_operator self); -template -product_operator operator*(const std::complex other, - const elementary_operator self); -template -operator_sum operator+(double other, const elementary_operator self); -template -operator_sum operator-(double other, const elementary_operator self); -template -product_operator operator*(double other, const elementary_operator self); +product_operator operator*(double other, const elementary_operator &self); +operator_sum operator+(double other, const elementary_operator &self); +operator_sum operator-(double other, const elementary_operator &self); +product_operator operator*(std::complex other, const elementary_operator &self); +operator_sum operator+(std::complex other, const elementary_operator &self); +operator_sum operator-(std::complex other, const elementary_operator &self); +product_operator operator*(const scalar_operator &other, const elementary_operator &self); +operator_sum operator+(const scalar_operator &other, const elementary_operator &self); +operator_sum operator-(const scalar_operator &other, const elementary_operator &self); /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian : public operator_sum { From 68c07c88f78aac63815de02796c8ca1d5fb41166 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 20:29:09 +0000 Subject: [PATCH 050/311] kinded of more cleaned up but left-hand arithmethics need more thought Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 18 +++++++----------- runtime/cudaq/dynamics/operator_sum.cpp | 10 ++++------ runtime/cudaq/dynamics/product_operators.cpp | 12 ++++-------- runtime/cudaq/operators.h | 14 +++++++------- 4 files changed, 22 insertions(+), 32 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index a13d0d1aa2..021e81e534 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -411,27 +411,23 @@ operator_sum operator+(std::complex other, const el } operator_sum operator-(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _other = { - other_scalar}; + std::vector> _other = {scalar_operator(other)}; return operator_sum({product_operator(_other), (-1. * self)}); } product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator({other, self}); + return product_operator({other, self}); } operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum({product_operator({other}), product_operator({self})}); + std::vector> _other = {other}; + std::vector> _self = {self}; + return operator_sum({product_operator(_other), product_operator(_self)}); } operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum( - {product_operator({other}), product_operator({-1. * self})}); + std::vector> _other = {other}; + return operator_sum({product_operator(_other), -1. * self}); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index e2c238e932..f83f09e680 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -312,7 +312,9 @@ operator_sum operator_sum::operator+(const product_operato template operator_sum operator_sum::operator-(const product_operator &other) const { - return *this + (-1. * other); + std::vector> combined_terms = terms; + combined_terms.push_back(other * (-1.)); + return operator_sum(combined_terms); } template @@ -357,13 +359,9 @@ operator_sum operator_sum::operator+(const operator_sum operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (-1 * other); + return *this + (other * (-1)); } -//template -//operator_sum operator_sum::operator-( -// const operator_sum &other) const; - template operator_sum operator_sum::operator*=(const operator_sum &other) { *this = *this * other; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 8db05cd0da..b91f05f9aa 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -163,7 +163,6 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } - // right-hand arithmetics template @@ -225,9 +224,7 @@ operator_sum product_operator::operator+(const scalar_oper template operator_sum product_operator::operator-(const scalar_operator &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, product_operator({-1. * other})}); } template @@ -253,9 +250,7 @@ operator_sum product_operator::operator+(const HandlerTy & template operator_sum product_operator::operator-(const HandlerTy &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, -1. * other}); } template @@ -281,7 +276,8 @@ operator_sum product_operator::operator+(const product_ope template operator_sum product_operator::operator-(const product_operator &other) const { - return operator_sum({*this, (-1. * other)}); + std::vector> combined_terms = {*this, -1. * other}; + return operator_sum(combined_terms); } template diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6769e4aabb..98319c9721 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -156,11 +156,11 @@ operator_sum operator+(const scalar_operator &other, const operator_s template operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -operator_sum operator*(const HandlerTy &other, const operator_sum self); +operator_sum operator*(const HandlerTy &other, const operator_sum &self); template -operator_sum operator+(const HandlerTy &other, const operator_sum self); +operator_sum operator+(const HandlerTy &other, const operator_sum &self); template -operator_sum operator-(const HandlerTy &other, const operator_sum self); +operator_sum operator-(const HandlerTy &other, const operator_sum &self); /// @brief Represents an operator expression consisting of a product of @@ -342,14 +342,14 @@ class scalar_operator { scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - scalar_operator operator*(const scalar_operator &other) const; - scalar_operator operator/(const scalar_operator &other) const; - scalar_operator operator+(const scalar_operator &other) const; - scalar_operator operator-(const scalar_operator &other) const; void operator*=(std::complex other); void operator/=(std::complex other); void operator+=(std::complex other); void operator-=(std::complex other); + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; void operator*=(const scalar_operator &other); void operator/=(const scalar_operator &other); void operator+=(const scalar_operator &other); From 45f69268b7083d298dcf60f8049ff8bfb22ce82d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 21:49:53 +0000 Subject: [PATCH 051/311] constrain the templates Signed-off-by: Bettina Heim --- runtime/cudaq/operators.h | 39 ++++++++++++++++++++++++++++++++++----- unittests/CMakeLists.txt | 11 ++++------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 98319c9721..be22981d26 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -15,24 +15,28 @@ #include #include #include +#include namespace cudaq { -template -class operator_sum; +class scalar_operator; + +class elementary_operator; template +requires std::derived_from class product_operator; -class elementary_operator; - -class scalar_operator; +template +requires std::derived_from +class operator_sum; /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. template // handler needs to inherit from operation_handler +requires std::derived_from class operator_sum { private: @@ -138,28 +142,40 @@ class operator_sum { }; template +requires std::derived_from operator_sum operator*(double other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(double other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(double other, const operator_sum &self); template +requires std::derived_from operator_sum operator*(std::complex other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(std::complex other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(std::complex other, const operator_sum &self); template +requires std::derived_from operator_sum operator*(const scalar_operator &other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self); template +requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self); @@ -168,6 +184,7 @@ operator_sum operator-(const HandlerTy &other, const operat /// quantum kernels, but they provide methods to convert them to data types /// that can. template // handler needs to inherit from operation_handler +requires std::derived_from class product_operator : public operator_sum { private: @@ -251,28 +268,40 @@ class product_operator : public operator_sum { }; template +requires std::derived_from product_operator operator*(double other, const product_operator &self); template +requires std::derived_from operator_sum operator+(double other, const product_operator &self); template +requires std::derived_from operator_sum operator-(double other, const product_operator &self); template +requires std::derived_from product_operator operator*(std::complex other, const product_operator &self); template +requires std::derived_from operator_sum operator+(std::complex other, const product_operator &self); template +requires std::derived_from operator_sum operator-(std::complex other, const product_operator &self); template +requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self); template +requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self); template +requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self); template +requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self); template +requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self); template +requires std::derived_from operator_sum operator-(const HandlerTy &other, const product_operator &self); diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ecc2d68116..77437e5d31 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -44,11 +44,6 @@ set(CUDAQ_RUNTIME_TEST_SOURCES common/NoiseModelTester.cpp integration/tracer_tester.cpp integration/gate_library_tester.cpp - dynamics/scalar_ops_simple.cpp - dynamics/scalar_ops_arithmetic.cpp - dynamics/elementary_ops_simple.cpp - dynamics/elementary_ops_arithmetic.cpp - dynamics/product_operators_arithmetic.cpp dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp @@ -80,8 +75,10 @@ macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) endif() target_link_libraries(${TEST_EXE_NAME} PUBLIC - nvqir-${NVQIR_BACKEND} nvqir - cudaq fmt::fmt-header-only + nvqir-${NVQIR_BACKEND} + nvqir + cudaq + fmt::fmt-header-only cudaq-platform-default cudaq-builder gtest_main From d663b02dfe68aba35c9dc9e81548878c7d2685a1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 22:50:51 +0000 Subject: [PATCH 052/311] making the left hand arithmetics friends Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 122 ++++++------- runtime/cudaq/dynamics/operator_sum.cpp | 158 ++++++++-------- runtime/cudaq/dynamics/product_operators.cpp | 142 +++++++-------- runtime/cudaq/dynamics/scalar_operators.cpp | 62 +++---- runtime/cudaq/operators.h | 172 ++++++++---------- 5 files changed, 317 insertions(+), 339 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 021e81e534..3f3805539c 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -277,6 +277,67 @@ matrix_2 elementary_operator::to_matrix( return m_ops[id].generator(dimensions, parameters); } +// left-hand arithmetics + +product_operator operator*(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +operator_sum operator+(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(std::complex other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +operator_sum operator+(std::complex other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(std::complex other, const elementary_operator &self) { + std::vector> _other = {scalar_operator(other)}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(const scalar_operator &other, const elementary_operator &self) { + return product_operator({other, self}); +} + +operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { + std::vector> _other = {other}; + std::vector> _self = {self}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { + std::vector> _other = {other}; + return operator_sum({product_operator(_other), -1. * self}); +} + // right-hand arithmetics product_operator elementary_operator::operator*(double other) const { @@ -369,65 +430,4 @@ operator_sum elementary_operator::operator-(const elementar return operator_sum({product_operator(_this), (-1. * other)}); } -// left-hand arithmetics - -product_operator operator*(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); -} - -operator_sum operator+(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); -} - -operator_sum operator-(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _other = { - other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); -} - -product_operator operator*(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); -} - -operator_sum operator+(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); -} - -operator_sum operator-(std::complex other, const elementary_operator &self) { - std::vector> _other = {scalar_operator(other)}; - return operator_sum({product_operator(_other), (-1. * self)}); -} - -product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator({other, self}); -} - -operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - std::vector> _self = {self}; - return operator_sum({product_operator(_other), product_operator(_self)}); -} - -operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - return operator_sum({product_operator(_other), -1. * self}); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index f83f09e680..5a2ea2780c 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -138,6 +138,85 @@ template class operator_sum; // return result; // } +// left-hand arithmetics + +template +operator_sum operator*(double other, const operator_sum &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(double other, const operator_sum &self) { + return scalar_operator(other) + self; +} + +template +operator_sum operator-(double other, const operator_sum &self) { + return scalar_operator(other) - self; +} + +template +operator_sum operator*(std::complex other, const operator_sum &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(std::complex other, const operator_sum &self) { + return scalar_operator(other) + self; +} + +template +operator_sum operator-(std::complex other, const operator_sum &self) { + return scalar_operator(other) - self; +} + +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + for (auto &term : terms) + term = other * term; + return operator_sum(terms); +} + +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); +} + +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self) { + auto negative_self = (-1. * self); + std::vector> terms = negative_self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); +} + +template +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * self; +} + +template +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + self; +} + +template +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - self; +} + // right-hand arithmetics template @@ -380,83 +459,4 @@ operator_sum operator_sum::operator+=(const operator_sum -operator_sum operator*(double other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(double other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(std::complex other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(std::complex other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(std::complex other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - for (auto &term : terms) - term = other * term; - return operator_sum(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self) { - auto negative_self = (-1. * self); - std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * self; -} - -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + self; -} - -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - self; -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index b91f05f9aa..1aba9c915a 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -163,6 +163,77 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } +// left-hand arithmetics + +template +product_operator operator*(double other, const product_operator &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(double other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); +} + +template +operator_sum operator-(double other, const product_operator &self) { + return scalar_operator(other) - self; +} + +template +product_operator operator*(const std::complex other, const product_operator &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(const std::complex other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); +} + +template +operator_sum operator-(const std::complex other, const product_operator &self) { + return scalar_operator(other) - self; +} + +template +product_operator operator*(const scalar_operator &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this scalar operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +operator_sum operator+(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); +} + +template +operator_sum operator-(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), (-1. * self)}); +} + +template +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this elementary operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + std::vector> new_term = {other}; + return operator_sum({product_operator(new_term), self}); +} + +template +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return other + (-1. * self); +} + // right-hand arithmetics template @@ -310,75 +381,4 @@ operator_sum product_operator::operator-(const operator_su return operator_sum(other_terms); } -// left-hand arithmetics - -template -product_operator operator*(double other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(double other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const std::complex other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(const std::complex other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const scalar_operator &other, const product_operator &self) { - std::vector> terms = - self.get_terms(); - /// Insert this scalar operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); -} - -template -operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), (-1. * self)}); -} - -template -product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector> terms = - self.get_terms(); - /// Insert this elementary operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - std::vector> new_term = {other}; - return operator_sum({product_operator(new_term), self}); -} - -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return other + (-1. * self); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index dbd1c046b7..0bab3de20f 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -50,6 +50,37 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +// left-hand arithmetics + +#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ + scalar_operator operator op(double other, const scalar_operator &self) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return other op self.evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(std::complex other, \ + const scalar_operator &self) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return other op self.evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); + // right-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ @@ -154,35 +185,4 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); -// left-hand arithmetics - -#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(double other, const scalar_operator &self) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(std::complex other, \ - const scalar_operator &self) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index be22981d26..6f61e9f342 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -70,6 +70,9 @@ class operator_sum { // template // TEval _evaluate(OperatorArithmetics &arithmetics) const; + /// @brief Return the operator_sum as a string. + std::string to_string() const; + /// @brief Return the `operator_sum` as a matrix. /// @arg `dimensions` : A mapping that specifies the number of levels, /// that is, the dimension of each degree of freedom @@ -119,8 +122,32 @@ class operator_sum { operator_sum operator+=(const operator_sum &other); operator_sum operator-=(const operator_sum &other); - /// @brief Return the operator_sum as a string. - std::string to_string() const; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-template-friend" +#endif +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif + friend operator_sum operator*(double other, const operator_sum &self); + friend operator_sum operator+(double other, const operator_sum &self); + friend operator_sum operator-(double other, const operator_sum &self); + friend operator_sum operator*(std::complex other, const operator_sum &self); + friend operator_sum operator+(std::complex other, const operator_sum &self); + friend operator_sum operator-(std::complex other, const operator_sum &self); + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif /// @brief Return the number of operator terms that make up this operator sum. int term_count() const { return terms.size(); } @@ -141,44 +168,6 @@ class operator_sum { std::vector> get_terms() const { return terms; } }; -template -requires std::derived_from -operator_sum operator*(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const operator_sum &self); - - /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types @@ -255,6 +244,33 @@ class product_operator : public operator_sum { operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-template-friend" +#endif +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif + friend product_operator operator*(double other, const product_operator &self); + friend operator_sum operator+(double other, const product_operator &self); + friend operator_sum operator-(double other, const product_operator &self); + friend product_operator operator*(std::complex other, const product_operator &self); + friend operator_sum operator+(std::complex other, const product_operator &self); + friend operator_sum operator-(std::complex other, const product_operator &self); + friend product_operator operator*(const scalar_operator &other, const product_operator &self); + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + friend product_operator operator*(const HandlerTy &other, const product_operator &self); + friend operator_sum operator+(const HandlerTy &other, const product_operator &self); + friend operator_sum operator-(const HandlerTy &other, const product_operator &self); +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they @@ -267,44 +283,6 @@ class product_operator : public operator_sum { bool operator==(product_operator other); }; -template -requires std::derived_from -product_operator operator*(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(double other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const product_operator &self); - - // FIXME: check if we really need the inheritance from prod operator, and if so what it should be // (check if this really should be its own class?) // -> replace elementary operator with the operator handler, the current elem op is the handler for custom op @@ -385,19 +363,20 @@ class scalar_operator { void operator-=(const scalar_operator &other); /// TODO: implement and test pow + friend scalar_operator operator*(double other, const scalar_operator &self); + friend scalar_operator operator/(double other, const scalar_operator &self); + friend scalar_operator operator+(double other, const scalar_operator &self); + friend scalar_operator operator-(double other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, const scalar_operator &self); + friend scalar_operator operator/(std::complex other, const scalar_operator &self); + friend scalar_operator operator+(std::complex other, const scalar_operator &self); + friend scalar_operator operator-(std::complex other, const scalar_operator &self); + // /// @brief Returns true if other is a scalar operator with the same // /// generator. // bool operator==(scalar_operator other); }; -scalar_operator operator*(double other, const scalar_operator &self); -scalar_operator operator/(double other, const scalar_operator &self); -scalar_operator operator+(double other, const scalar_operator &self); -scalar_operator operator-(double other, const scalar_operator &self); -scalar_operator operator*(std::complex other, const scalar_operator &self); -scalar_operator operator/(std::complex other, const scalar_operator &self); -scalar_operator operator+(std::complex other, const scalar_operator &self); -scalar_operator operator-(std::complex other, const scalar_operator &self); class elementary_operator : public product_operator { @@ -464,6 +443,16 @@ class elementary_operator : public product_operator { operator_sum operator+(const elementary_operator &other) const; operator_sum operator-(const elementary_operator &other) const; + friend product_operator operator*(double other, const elementary_operator &self); + friend operator_sum operator+(double other, const elementary_operator &self); + friend operator_sum operator-(double other, const elementary_operator &self); + friend product_operator operator*(std::complex other, const elementary_operator &self); + friend operator_sum operator+(std::complex other, const elementary_operator &self); + friend operator_sum operator-(std::complex other, const elementary_operator &self); + friend product_operator operator*(const scalar_operator &other, const elementary_operator &self); + friend operator_sum operator+(const scalar_operator &other, const elementary_operator &self); + friend operator_sum operator-(const scalar_operator &other, const elementary_operator &self); + /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. bool operator==(elementary_operator other); @@ -522,17 +511,6 @@ class elementary_operator : public product_operator { } }; -// Reverse order arithmetic for elementary operators against pure scalars. -product_operator operator*(double other, const elementary_operator &self); -operator_sum operator+(double other, const elementary_operator &self); -operator_sum operator-(double other, const elementary_operator &self); -product_operator operator*(std::complex other, const elementary_operator &self); -operator_sum operator+(std::complex other, const elementary_operator &self); -operator_sum operator-(std::complex other, const elementary_operator &self); -product_operator operator*(const scalar_operator &other, const elementary_operator &self); -operator_sum operator+(const scalar_operator &other, const elementary_operator &self); -operator_sum operator-(const scalar_operator &other, const elementary_operator &self); - /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian : public operator_sum { public: From 3dc2d504d3d9478e22ebae2c4c1f31e01c06daa9 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 15 Jan 2025 13:19:28 +0000 Subject: [PATCH 053/311] cleaned up assignments Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 2 + runtime/cudaq/dynamics/operator_sum.cpp | 48 ++++++------ runtime/cudaq/dynamics/product_operators.cpp | 16 ++-- runtime/cudaq/dynamics/scalar_operators.cpp | 58 +++++++------- runtime/cudaq/operators.h | 79 +++++++++++--------- 5 files changed, 103 insertions(+), 100 deletions(-) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 63129f246f..3818446488 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -32,6 +32,8 @@ endif() add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) +target_compile_definitions(${LIBRARY_NAME} PRIVATE -DCUDAQ_INSTANTIATE_TEMPLATES) + target_include_directories(${LIBRARY_NAME} PUBLIC $ diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 5a2ea2780c..0fcf5fe70a 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -23,7 +23,7 @@ namespace cudaq { // std::vector scalars; // std::vector non_scalars; -// for (const auto &op : prod.get_terms()) { +// for (const auto &op : prod.get_operators()) { // if (std::holds_alternative(op)) { // scalars.push_back(*std::get(op)); // } else { @@ -83,7 +83,7 @@ namespace cudaq { // std::vector operator_sum::degrees() const { // std::set unique_degrees; // for (const auto &term : terms) { -// for (const auto &op : term.get_terms()) { +// for (const auto &op : term.get_operators()) { // unique_degrees.insert(op.get_degrees().begin(), // op.get_degrees().end()); // } @@ -96,7 +96,7 @@ namespace cudaq { // std::map operator_sum::parameters() const { // std::map param_map; // for (const auto &term : terms) { -// for (const auto &op : term.get_terms()) { +// for (const auto &op : term.get_operators()) { // auto op_params = op.parameters(); // param_map.insert(op_params.begin(), op.params.end()); // } @@ -109,15 +109,13 @@ namespace cudaq { // bool operator_sum::_is_spinop() const { // return std::all_of( // terms.begin(), terms.end(), [](product_operator &term) { -// return std::all_of(term.get_terms().begin(), -// term.get_terms().end(), +// return std::all_of(term.get_operators().begin(), +// term.get_operators().end(), // [](const Operator &op) { return op.is_spinop(); // }); // }); // } -template class operator_sum; - // evaluations /// FIXME: @@ -235,19 +233,19 @@ operator_sum operator_sum::operator-(double other) const { } template -operator_sum operator_sum::operator*=(double other) { +operator_sum& operator_sum::operator*=(double other) { *this *= scalar_operator(other); return *this; } template -operator_sum operator_sum::operator+=(double other) { +operator_sum& operator_sum::operator+=(double other) { *this += scalar_operator(other); return *this; } template -operator_sum operator_sum::operator-=(double other) { +operator_sum& operator_sum::operator-=(double other) { *this -= scalar_operator(other); return *this; } @@ -268,19 +266,19 @@ operator_sum operator_sum::operator-(std::complex } template -operator_sum operator_sum::operator*=(std::complex other) { +operator_sum& operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); return *this; } template -operator_sum operator_sum::operator+=(std::complex other) { +operator_sum& operator_sum::operator+=(std::complex other) { *this += scalar_operator(other); return *this; } template -operator_sum operator_sum::operator-=(std::complex other) { +operator_sum& operator_sum::operator-=(std::complex other) { *this -= scalar_operator(other); return *this; } @@ -309,19 +307,19 @@ operator_sum operator_sum::operator-(const scalar_operator } template -operator_sum operator_sum::operator*=(const scalar_operator &other) { +operator_sum& operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(const scalar_operator &other) { +operator_sum& operator_sum::operator+=(const scalar_operator &other) { *this = *this + other; return *this; } template -operator_sum operator_sum::operator-=(const scalar_operator &other) { +operator_sum& operator_sum::operator-=(const scalar_operator &other) { *this = *this - other; return *this; } @@ -352,13 +350,13 @@ operator_sum operator_sum::operator-(const HandlerTy &othe } template -operator_sum operator_sum::operator*=(const HandlerTy &other) { +operator_sum& operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(const HandlerTy &other) { +operator_sum& operator_sum::operator+=(const HandlerTy &other) { std::vector> _other = { other}; *this = *this + product_operator(_other); @@ -366,7 +364,7 @@ operator_sum operator_sum::operator+=(const HandlerTy &oth } template -operator_sum operator_sum::operator-=(const HandlerTy &other) { +operator_sum& operator_sum::operator-=(const HandlerTy &other) { std::vector> _other = { other}; *this = *this - product_operator(_other); @@ -397,19 +395,19 @@ operator_sum operator_sum::operator-(const product_operato } template -operator_sum operator_sum::operator*=(const product_operator &other) { +operator_sum& operator_sum::operator*=(const product_operator &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(const product_operator &other) { +operator_sum& operator_sum::operator+=(const product_operator &other) { *this = *this + other; return *this; } template -operator_sum operator_sum::operator-=(const product_operator &other) { +operator_sum& operator_sum::operator-=(const product_operator &other) { *this = *this - other; return *this; } @@ -442,19 +440,19 @@ operator_sum operator_sum::operator-(const operator_sum -operator_sum operator_sum::operator*=(const operator_sum &other) { +operator_sum& operator_sum::operator*=(const operator_sum &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator-=(const operator_sum &other) { +operator_sum& operator_sum::operator-=(const operator_sum &other) { *this = *this - other; return *this; } template -operator_sum operator_sum::operator+=(const operator_sum &other) { +operator_sum& operator_sum::operator+=(const operator_sum &other) { *this = *this + other; return *this; } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 1aba9c915a..af83948370 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -142,8 +142,6 @@ matrix_2 product_operator::to_matrix( // return out; // } -template class product_operator; - // Degrees property template std::vector product_operator::degrees() const { @@ -198,7 +196,7 @@ operator_sum operator-(const std::complex other, const produc template product_operator operator*(const scalar_operator &other, const product_operator &self) { std::vector> terms = - self.get_terms(); + self.get_operators(); /// Insert this scalar operator to the front of the terms list. terms.insert(terms.begin(), other); return product_operator(terms); @@ -217,7 +215,7 @@ operator_sum operator-(const scalar_operator &other, const product_op template product_operator operator*(const HandlerTy &other, const product_operator &self) { std::vector> terms = - self.get_terms(); + self.get_operators(); /// Insert this elementary operator to the front of the terms list. terms.insert(terms.begin(), other); return product_operator(terms); @@ -252,7 +250,7 @@ operator_sum product_operator::operator-(double other) con } template -product_operator product_operator::operator*=(double other) { +product_operator& product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } @@ -273,7 +271,7 @@ operator_sum product_operator::operator-(std::complex -product_operator product_operator::operator*=(std::complex other) { +product_operator& product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } @@ -299,7 +297,7 @@ operator_sum product_operator::operator-(const scalar_oper } template -product_operator product_operator::operator*=(const scalar_operator &other) { +product_operator& product_operator::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } @@ -325,7 +323,7 @@ operator_sum product_operator::operator-(const HandlerTy & } template -product_operator product_operator::operator*=(const HandlerTy &other) { +product_operator& product_operator::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } @@ -352,7 +350,7 @@ operator_sum product_operator::operator-(const product_ope } template -product_operator product_operator::operator*=(const product_operator &other) { +product_operator& product_operator::operator*=(const product_operator &other) { *this = *this * other; return *this; } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 0bab3de20f..24ce886a65 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -84,11 +84,10 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); // right-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ - scalar_operator operator op(const scalar_operator &self, double other) { \ + scalar_operator scalar_operator::operator op(double other) const { \ auto newGenerator = \ [&](std::map> parameters) { \ - return self \ - .evaluate(parameters) op other; \ + return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } @@ -99,16 +98,16 @@ ARITHMETIC_OPERATIONS_DOUBLES(+); ARITHMETIC_OPERATIONS_DOUBLES(-); #define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, double other) { \ + scalar_operator& scalar_operator::operator op(double other) { \ /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ + * we can modify the generator in-place. */ \ + scalar_operator prevSelf(*this); \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other; \ + return prevSelf.evaluate(parameters) op other; \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + this->generator = ScalarCallbackFunction(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); @@ -117,11 +116,11 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const std::complex other) { \ + scalar_operator scalar_operator::operator op( \ + const std::complex other) const{ \ auto newGenerator = \ [&](std::map> parameters) { \ - return self.evaluate(parameters) op other; \ + return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } @@ -132,16 +131,17 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, std::complex other) { \ + scalar_operator& scalar_operator::operator op( \ + std::complex other) { \ /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ + * we can modify the generator in-place. */ \ + scalar_operator prevSelf(*this); \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other; \ + return prevSelf.evaluate(parameters) op other; \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + this->generator = ScalarCallbackFunction(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); @@ -150,13 +150,11 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const scalar_operator &other) { \ + scalar_operator scalar_operator::operator op( \ + const scalar_operator &other) const { \ auto newGenerator = \ [&](std::map> parameters) { \ - return self \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ + return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } @@ -167,17 +165,17 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, const scalar_operator &other) { \ + scalar_operator& scalar_operator::operator op( \ + const scalar_operator &other) { \ /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ + * that we can modify the generator in-place. */ \ + scalar_operator prevSelf(*this); \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ + return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + this->generator = ScalarCallbackFunction(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6f61e9f342..d356c03598 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -88,39 +88,39 @@ class operator_sum { operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - operator_sum operator*=(double other); - operator_sum operator+=(double other); - operator_sum operator-=(double other); + operator_sum& operator*=(double other); + operator_sum& operator+=(double other); + operator_sum& operator-=(double other); operator_sum operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - operator_sum operator*=(std::complex other); - operator_sum operator+=(std::complex other); - operator_sum operator-=(std::complex other); + operator_sum& operator*=(std::complex other); + operator_sum& operator+=(std::complex other); + operator_sum& operator-=(std::complex other); operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*=(const scalar_operator &other); - operator_sum operator+=(const scalar_operator &other); - operator_sum operator-=(const scalar_operator &other); + operator_sum& operator*=(const scalar_operator &other); + operator_sum& operator+=(const scalar_operator &other); + operator_sum& operator-=(const scalar_operator &other); operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum operator*=(const HandlerTy &other); - operator_sum operator+=(const HandlerTy &other); - operator_sum operator-=(const HandlerTy &other); + operator_sum& operator*=(const HandlerTy &other); + operator_sum& operator+=(const HandlerTy &other); + operator_sum& operator-=(const HandlerTy &other); operator_sum operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - operator_sum operator*=(const product_operator &other); - operator_sum operator+=(const product_operator &other); - operator_sum operator-=(const product_operator &other); + operator_sum& operator*=(const product_operator &other); + operator_sum& operator+=(const product_operator &other); + operator_sum& operator-=(const product_operator &other); operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; - operator_sum operator*=(const operator_sum &other); - operator_sum operator+=(const operator_sum &other); - operator_sum operator-=(const operator_sum &other); + operator_sum& operator*=(const operator_sum &other); + operator_sum& operator+=(const operator_sum &other); + operator_sum& operator-=(const operator_sum &other); #ifdef __clang__ #pragma clang diagnostic push @@ -201,7 +201,7 @@ class product_operator : public operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() const { + std::vector> get_operators() const { return ops; }; @@ -223,23 +223,23 @@ class product_operator : public operator_sum { product_operator operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - product_operator operator*=(double other); + product_operator& operator*=(double other); product_operator operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - product_operator operator*=(std::complex other); + product_operator& operator*=(std::complex other); product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - product_operator operator*=(const scalar_operator &other); + product_operator& operator*=(const scalar_operator &other); product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator operator*=(const HandlerTy &other); + product_operator& operator*=(const HandlerTy &other); product_operator operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - product_operator operator*=(const product_operator &other); + product_operator& operator*=(const product_operator &other); operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; @@ -341,26 +341,26 @@ class scalar_operator { scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; scalar_operator operator-(double other) const; - void operator*=(double other); - void operator/=(double other); - void operator+=(double other); - void operator-=(double other); + scalar_operator& operator*=(double other); + scalar_operator& operator/=(double other); + scalar_operator& operator+=(double other); + scalar_operator& operator-=(double other); scalar_operator operator*(std::complex other) const; scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - void operator*=(std::complex other); - void operator/=(std::complex other); - void operator+=(std::complex other); - void operator-=(std::complex other); + scalar_operator& operator*=(std::complex other); + scalar_operator& operator/=(std::complex other); + scalar_operator& operator+=(std::complex other); + scalar_operator& operator-=(std::complex other); scalar_operator operator*(const scalar_operator &other) const; scalar_operator operator/(const scalar_operator &other) const; scalar_operator operator+(const scalar_operator &other) const; scalar_operator operator-(const scalar_operator &other) const; - void operator*=(const scalar_operator &other); - void operator/=(const scalar_operator &other); - void operator+=(const scalar_operator &other); - void operator-=(const scalar_operator &other); + scalar_operator& operator*=(const scalar_operator &other); + scalar_operator& operator/=(const scalar_operator &other); + scalar_operator& operator+=(const scalar_operator &other); + scalar_operator& operator-=(const scalar_operator &other); /// TODO: implement and test pow friend scalar_operator operator*(double other, const scalar_operator &self); @@ -556,5 +556,12 @@ class rydberg_hamiltonian : public operator_sum { scalar_operator delta_global; std::optional>> delta_local; }; +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +template class product_operator; +template class operator_sum; +#else +extern template class product_operator; +extern template class operator_sum; +#endif } // namespace cudaq \ No newline at end of file From 3d52d479afb264f59fe3debee77d9847d7a9de20 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 16 Jan 2025 13:02:59 +0000 Subject: [PATCH 054/311] fixing a some types Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 18 +++++++++--------- runtime/cudaq/operators.h | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 0fcf5fe70a..4922562910 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -192,25 +192,25 @@ operator_sum operator-(const scalar_operator &other, const operator_s } template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; auto selfOpSum = operator_sum(_prods); return selfOpSum * self; } template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; auto selfOpSum = operator_sum(_prods); return selfOpSum + self; } template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; auto selfOpSum = operator_sum(_prods); return selfOpSum - self; } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d356c03598..5de4c7b586 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -139,9 +139,9 @@ class operator_sum { friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif @@ -261,9 +261,9 @@ class product_operator : public operator_sum { friend product_operator operator*(const scalar_operator &other, const product_operator &self); friend operator_sum operator+(const scalar_operator &other, const product_operator &self); friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - friend product_operator operator*(const HandlerTy &other, const product_operator &self); - friend operator_sum operator+(const HandlerTy &other, const product_operator &self); - friend operator_sum operator-(const HandlerTy &other, const product_operator &self); + friend product_operator operator*(const HandlerTy &other, const product_operator &self); + friend operator_sum operator+(const HandlerTy &other, const product_operator &self); + friend operator_sum operator-(const HandlerTy &other, const product_operator &self); #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif From 6c0465065af0ef8482b635dabbe956ffd0c0a579 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 16 Jan 2025 18:19:17 +0000 Subject: [PATCH 055/311] this builds, but needs to be cleaned up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 13 +- runtime/cudaq/dynamics/instantiations.cpp | 815 +++++++++++++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 320 -------- runtime/cudaq/dynamics/product_operators.cpp | 218 ----- runtime/cudaq/operators.h | 133 +++ 5 files changed, 955 insertions(+), 544 deletions(-) create mode 100644 runtime/cudaq/dynamics/instantiations.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 3818446488..3bbfb4eec0 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,18 +11,19 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp - elementary_operators.cpp - product_operators.cpp - operator_sum.cpp - schedule.cpp - definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp cudm_time_stepper.cpp runge_kutta_integrator.cpp + definition.cpp + scalar_operators.cpp + elementary_operators.cpp + product_operators.cpp + operator_sum.cpp + instantiations.cpp + schedule.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/instantiations.cpp new file mode 100644 index 0000000000..71add82c27 --- /dev/null +++ b/runtime/cudaq/dynamics/instantiations.cpp @@ -0,0 +1,815 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +// product operator left-hand arithmetics + +template +requires std::derived_from +product_operator operator*(const scalar_operator &other, const product_operator &self) { + std::vector> terms = + self.get_operators(); + /// Insert this scalar operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); +} + +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self * (-1.)}); +} + +template +requires std::derived_from +product_operator operator*(double other, const product_operator &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(double other, const product_operator &self) { + return operator_sum({product_operator({scalar_operator(other)}), self}); +} + +template +requires std::derived_from +operator_sum operator-(double other, const product_operator &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +product_operator operator*(const std::complex other, const product_operator &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(const std::complex other, const product_operator &self) { + return operator_sum({product_operator({scalar_operator(other)}), self}); +} + +template +requires std::derived_from +operator_sum operator-(const std::complex other, const product_operator &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector> terms = + self.get_operators(); + /// Insert this elementary operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); +} + +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return (self * (-1.)) + other; +} + +// operator sum left-hand arithmetics + +template +requires std::derived_from +operator_sum operator*(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + for (auto &term : terms) + term = term * other; + return operator_sum(terms); +} + +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + terms.insert(terms.begin(), product_operator({other})); + return operator_sum(terms); +} + +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const operator_sum &self) { + auto negative_self = self * (-1.); + std::vector> terms = negative_self.get_terms(); + terms.insert(terms.begin(), product_operator({other})); + return operator_sum(terms); +} + +template +requires std::derived_from +operator_sum operator*(double other, const operator_sum &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(double other, const operator_sum &self) { + return self + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator-(double other, const operator_sum &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator*(std::complex other, const operator_sum &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(std::complex other, const operator_sum &self) { + return self + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator-(std::complex other, const operator_sum &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * self; +} + +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + self; +} + +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - self; +} + +// operator sum right-hand arithmetics + +template +requires std::derived_from +operator_sum operator_sum::operator*(double other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(double other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(double other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(double other) { + *this *= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(double other) { + *this += scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(double other) { + *this -= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(std::complex other) { + *this *= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(std::complex other) { + *this += scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(std::complex other) { + *this -= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const scalar_operator &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const scalar_operator &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const scalar_operator &other) const { + return *this + (other * (-1.0)); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const scalar_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const scalar_operator &other) { + *this = *this + other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const scalar_operator &other) { + *this = *this - other; + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const HandlerTy &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const HandlerTy &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other * (-1.)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const HandlerTy &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this + product_operator(_other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this - product_operator(_other); + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const product_operator &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other * (-1.)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const product_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const product_operator &other) { + *this = *this + other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const product_operator &other) { + *this = *this - other; + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const operator_sum &other) const { + auto self_terms = terms; + std::vector> product_terms; + auto other_terms = other.get_terms(); + for (auto &term : self_terms) { + for (auto &other_term : other_terms) { + product_terms.push_back(term * other_term); + } + } + return operator_sum(product_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector> combined_terms = terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.terms.begin()), + std::make_move_iterator(other.terms.end())); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const operator_sum &other) const { + return *this + (other * (-1)); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const operator_sum &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const operator_sum &other) { + *this = *this - other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const operator_sum &other) { + *this = *this + other; + return *this; +} + + +// product operator right-hand arithmetics + +template +requires std::derived_from +product_operator product_operator::operator*(double other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(double other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(double other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(double other) { + *this = *this * scalar_operator(other); + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(std::complex other) { + *this = *this * scalar_operator(other); + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(const scalar_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const scalar_operator &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const scalar_operator &other) const { + return operator_sum({*this, product_operator({other * (-1.)})}); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(const scalar_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(const HandlerTy &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const HandlerTy &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const HandlerTy &other) const { + return operator_sum({*this, other * (-1.)}); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(const HandlerTy &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(const product_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.ops.begin()), + std::make_move_iterator(other.ops.end())); + return product_operator(combined_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const product_operator &other) const { + return operator_sum({*this, other}); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const product_operator &other) const { + std::vector> combined_terms = {*this, other * (-1.)}; + return operator_sum(combined_terms); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(const product_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum product_operator::operator*(const operator_sum &other) const { + std::vector> other_terms = other.get_terms(); + for (auto &term : other_terms) { + term = *this * term; + } + return operator_sum(other_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const operator_sum &other) const { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const operator_sum &other) const { + auto negative_other = other * (-1.); + std::vector> other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + + +// instantiations + +template +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); +template +product_operator operator*(const elementary_operator &other, const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator+(std::complex other, const product_operator &self); +template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +operator_sum operator-(std::complex other, const product_operator &self); +template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); +template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); +template +operator_sum operator-(std::complex other, const operator_sum &self); +template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); + +template +operator_sum operator_sum::operator*(double other) const; +template +operator_sum operator_sum::operator+(double other) const; +template +operator_sum operator_sum::operator-(double other) const; +template +operator_sum& operator_sum::operator*=(double other); +template +operator_sum& operator_sum::operator+=(double other); +template +operator_sum& operator_sum::operator-=(double other); +template +operator_sum operator_sum::operator*(std::complex other) const; +template +operator_sum operator_sum::operator+(std::complex other) const; +template +operator_sum operator_sum::operator-(std::complex other) const; +template +operator_sum& operator_sum::operator*=(std::complex other); +template +operator_sum& operator_sum::operator+=(std::complex other); +template +operator_sum& operator_sum::operator-=(std::complex other); +template +operator_sum operator_sum::operator*(const scalar_operator &other) const; +template +operator_sum operator_sum::operator+(const scalar_operator &other) const; +template +operator_sum operator_sum::operator-(const scalar_operator &other) const; +template +operator_sum& operator_sum::operator*=(const scalar_operator &other); +template +operator_sum& operator_sum::operator+=(const scalar_operator &other); +template +operator_sum& operator_sum::operator-=(const scalar_operator &other); +template +operator_sum operator_sum::operator*(const elementary_operator &other) const; +template +operator_sum operator_sum::operator+(const elementary_operator &other) const; +template +operator_sum operator_sum::operator-(const elementary_operator &other) const; +template +operator_sum& operator_sum::operator*=(const elementary_operator &other); +template +operator_sum& operator_sum::operator+=(const elementary_operator &other); +template +operator_sum& operator_sum::operator-=(const elementary_operator &other); +template +operator_sum operator_sum::operator*(const product_operator &other) const; +template +operator_sum operator_sum::operator+(const product_operator &other) const; +template +operator_sum operator_sum::operator-(const product_operator &other) const; +template +operator_sum& operator_sum::operator*=(const product_operator &other); +template +operator_sum& operator_sum::operator+=(const product_operator &other); +template +operator_sum& operator_sum::operator-=(const product_operator &other); +template +operator_sum operator_sum::operator*(const operator_sum &other) const; +template +operator_sum operator_sum::operator+(const operator_sum &other) const; +template +operator_sum operator_sum::operator-(const operator_sum &other) const; +template +operator_sum& operator_sum::operator*=(const operator_sum &other); +template +operator_sum& operator_sum::operator-=(const operator_sum &other); +template +operator_sum& operator_sum::operator+=(const operator_sum &other); + +template +product_operator product_operator::operator*(double other) const; +template +operator_sum product_operator::operator+(double other) const; +template +operator_sum product_operator::operator-(double other) const; +template +product_operator& product_operator::operator*=(double other); +template +product_operator product_operator::operator*(std::complex other) const; +template +operator_sum product_operator::operator+(std::complex other) const; +template +operator_sum product_operator::operator-(std::complex other) const; +template +product_operator& product_operator::operator*=(std::complex other); +template +product_operator product_operator::operator*(const scalar_operator &other) const; +template +operator_sum product_operator::operator+(const scalar_operator &other) const; +template +operator_sum product_operator::operator-(const scalar_operator &other) const; +template +product_operator& product_operator::operator*=(const scalar_operator &other); +template +product_operator product_operator::operator*(const elementary_operator &other) const; +template +operator_sum product_operator::operator+(const elementary_operator &other) const; +template +operator_sum product_operator::operator-(const elementary_operator &other) const; +template +product_operator& product_operator::operator*=(const elementary_operator &other); +template +product_operator product_operator::operator*(const product_operator &other) const; +template +operator_sum product_operator::operator+(const product_operator &other) const; +template +operator_sum product_operator::operator-(const product_operator &other) const; +template +product_operator& product_operator::operator*=(const product_operator &other); +template +operator_sum product_operator::operator*(const operator_sum &other) const; +template +operator_sum product_operator::operator+(const operator_sum &other) const; +template +operator_sum product_operator::operator-(const operator_sum &other) const; + +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 4922562910..f2c7b75311 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -136,325 +136,5 @@ namespace cudaq { // return result; // } -// left-hand arithmetics - -template -operator_sum operator*(double other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(double other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(std::complex other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(std::complex other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(std::complex other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - for (auto &term : terms) - term = other * term; - return operator_sum(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self) { - auto negative_self = (-1. * self); - std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * self; -} - -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + self; -} - -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - self; -} - -// right-hand arithmetics - -template -operator_sum operator_sum::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(double other) const { - return *this - scalar_operator(other); -} - -template -operator_sum& operator_sum::operator*=(double other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(double other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(double other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum operator_sum::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - -template -operator_sum& operator_sum::operator*=(std::complex other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(std::complex other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(std::complex other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const scalar_operator &other) const { - return *this + (-1.0 * other); -} - -template -operator_sum& operator_sum::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const scalar_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const scalar_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back((-1. * other)); - return operator_sum(combined_terms); -} - -template -operator_sum& operator_sum::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this + product_operator(_other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this - product_operator(_other); - return *this; -} - -template -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back(other); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const product_operator &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back(other * (-1.)); - return operator_sum(combined_terms); -} - -template -operator_sum& operator_sum::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const product_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const product_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = terms; - std::vector> product_terms; - auto other_terms = other.get_terms(); - for (auto &term : self_terms) { - for (auto &other_term : other_terms) { - product_terms.push_back(term * other_term); - } - } - return operator_sum(product_terms); -} - -template -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = terms; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.terms.begin()), - std::make_move_iterator(other.terms.end())); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (other * (-1)); -} - -template -operator_sum& operator_sum::operator*=(const operator_sum &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const operator_sum &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const operator_sum &other) { - *this = *this + other; - return *this; -} } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index af83948370..1e9205c592 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -161,222 +161,4 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } -// left-hand arithmetics - -template -product_operator operator*(double other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(double other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const std::complex other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(const std::complex other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const scalar_operator &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this scalar operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); -} - -template -operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), (-1. * self)}); -} - -template -product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this elementary operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - std::vector> new_term = {other}; - return operator_sum({product_operator(new_term), self}); -} - -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return other + (-1. * self); -} - -// right-hand arithmetics - -template -product_operator product_operator::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(double other) const { - return *this - scalar_operator(other); -} - -template -product_operator& product_operator::operator*=(double other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator product_operator::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - -template -product_operator& product_operator::operator*=(std::complex other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator product_operator::operator*(const scalar_operator &other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); -} - -template -operator_sum product_operator::operator+(const scalar_operator &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); -} - -template -operator_sum product_operator::operator-(const scalar_operator &other) const { - return operator_sum({*this, product_operator({-1. * other})}); -} - -template -product_operator& product_operator::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -product_operator product_operator::operator*(const HandlerTy &other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); -} - -template -operator_sum product_operator::operator+(const HandlerTy &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); -} - -template -operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum({*this, -1. * other}); -} - -template -product_operator& product_operator::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - -template -product_operator product_operator::operator*(const product_operator &other) const { - std::vector> - combined_terms = ops; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.ops.begin()), - std::make_move_iterator(other.ops.end())); - return product_operator(combined_terms); -} - -template -operator_sum product_operator::operator+(const product_operator &other) const { - return operator_sum({*this, other}); -} - -template -operator_sum product_operator::operator-(const product_operator &other) const { - std::vector> combined_terms = {*this, -1. * other}; - return operator_sum(combined_terms); -} - -template -product_operator& product_operator::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum product_operator::operator*(const operator_sum &other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) { - term = *this * term; - } - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator+(const operator_sum &other) const { - std::vector other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator-(const operator_sum &other) const { - auto negative_other = (-1. * other); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 5de4c7b586..d5928ff743 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -31,6 +31,135 @@ template requires std::derived_from class operator_sum; +template +requires std::derived_from +product_operator operator*(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(double other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const product_operator &self); + +template +requires std::derived_from +operator_sum operator*(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const operator_sum &self); + + +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +#else +extern template +product_operator operator*(double other, const product_operator &self); +extern template +operator_sum operator+(double other, const product_operator &self); +extern template +operator_sum operator-(double other, const product_operator &self); +extern template +product_operator operator*(std::complex other, const product_operator &self); +extern template +operator_sum operator+(std::complex other, const product_operator &self); +extern template +operator_sum operator-(std::complex other, const product_operator &self); +extern template +product_operator operator*(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +extern template +product_operator operator*(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + +extern template +operator_sum operator*(double other, const operator_sum &self); +extern template +operator_sum operator+(double other, const operator_sum &self); +extern template +operator_sum operator-(double other, const operator_sum &self); +extern template +operator_sum operator*(std::complex other, const operator_sum &self); +extern template +operator_sum operator+(std::complex other, const operator_sum &self); +extern template +operator_sum operator-(std::complex other, const operator_sum &self); +extern template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); +#endif + + /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -130,6 +259,7 @@ class operator_sum { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-template-friend" #endif +/* friend operator_sum operator*(double other, const operator_sum &self); friend operator_sum operator+(double other, const operator_sum &self); friend operator_sum operator-(double other, const operator_sum &self); @@ -142,6 +272,7 @@ class operator_sum { friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); +*/ #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif @@ -252,6 +383,7 @@ class product_operator : public operator_sum { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-template-friend" #endif +/* friend product_operator operator*(double other, const product_operator &self); friend operator_sum operator+(double other, const product_operator &self); friend operator_sum operator-(double other, const product_operator &self); @@ -264,6 +396,7 @@ class product_operator : public operator_sum { friend product_operator operator*(const HandlerTy &other, const product_operator &self); friend operator_sum operator+(const HandlerTy &other, const product_operator &self); friend operator_sum operator-(const HandlerTy &other, const product_operator &self); +*/ #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif From 0cb84a8f977536c366d84658a4eec98f6bb2f8a2 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 17 Jan 2025 13:23:19 +0000 Subject: [PATCH 056/311] scalar ops tests Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/scalar_operators.cpp | 18 +++++++++--------- unittests/dynamics/scalar_ops_arithmetic.cpp | 13 +++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 24ce886a65..7eacac4775 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -55,7 +55,7 @@ matrix_2 scalar_operator::to_matrix( #define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ scalar_operator operator op(double other, const scalar_operator &self) { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -70,7 +70,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); scalar_operator operator op(std::complex other, \ const scalar_operator &self) { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -86,7 +86,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ scalar_operator scalar_operator::operator op(double other) const { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -103,7 +103,7 @@ ARITHMETIC_OPERATIONS_DOUBLES(-); * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ this->generator = ScalarCallbackFunction(newGenerator); \ @@ -117,9 +117,9 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ scalar_operator scalar_operator::operator op( \ - const std::complex other) const{ \ + std::complex other) const{ \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -137,7 +137,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ this->generator = ScalarCallbackFunction(newGenerator); \ @@ -153,7 +153,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); scalar_operator scalar_operator::operator op( \ const scalar_operator &other) const { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -171,7 +171,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); * that we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ }; \ this->generator = ScalarCallbackFunction(newGenerator); \ diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index edd4f9e675..00f93c1fc8 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -31,6 +31,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { auto new_scalar_op = value_1 + scalar_op; // function + scalar_op; auto reverse_order_op = scalar_op + value_1; + EXPECT_NEAR(std::abs(scalar_op.evaluate({})), std::abs(value_0), 1e-5); auto got_value = new_scalar_op.evaluate({}); auto got_value_1 = reverse_order_op.evaluate({}); @@ -156,13 +157,13 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { auto got_value = new_scalar_op.evaluate({}); auto got_value_1 = reverse_order_op.evaluate({}); - EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); - EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 / value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; auto got_value_third = third_op.evaluate({}); - auto want_value = (value_2 / value_3) / (value_3 / value_2); + auto want_value = (value_3 / value_2) / (value_2 / value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -176,13 +177,13 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { auto got_value = new_scalar_op.evaluate({{"value", value_1}}); auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); - EXPECT_NEAR(std::abs(got_value), std::abs(value_1 / value_3), 1e-5); - EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 / value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; auto got_value_third = third_op.evaluate({{"value", value_1}}); - auto want_value = (value_1 / value_3) / (value_3 / value_1); + auto want_value = (value_3 / value_1) / (value_1 / value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } From a5399d38ed3d6b85a13478708348b3fa2cbf5fb4 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 23 Jan 2025 16:35:21 +0000 Subject: [PATCH 057/311] to be deleted again - just some notes Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/memory.cpp | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 runtime/cudaq/dynamics/memory.cpp diff --git a/runtime/cudaq/dynamics/memory.cpp b/runtime/cudaq/dynamics/memory.cpp new file mode 100644 index 0000000000..ffb0bfeaf2 --- /dev/null +++ b/runtime/cudaq/dynamics/memory.cpp @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// FILE TO BE DELETED - THERE ARE JUST SOME NOTES/EXPERIMENTS TO CHECK HOW TO AVOID UNNECESSARY COPIES + +#include +#include +#include +#include // enable_if, conjuction + +template +using are_same = std::conjunction...>; + +class Foo { +public: + int i; + Foo() = default; + Foo(int i) : i(i) {} + Foo(const Foo &other) : i(other.i) { + std::cout << "copy Foo" << std::endl; + } +}; + +class Baz { +public: + Foo f; + Baz(const Foo &foo) : f(foo) {} + + Baz(const Baz& other) : f(other.f) { + std::cout << "copy Baz" << std::endl; + } +}; + +class Bar { +private: + + void aggregate(const Baz& head) { + std::cout << "got last " << head.f.i << std::endl; + items.push_back(head.f); + } + + template + void aggregate(const Baz &head, Args&& ... args) + { + std::cout << "got " << head.f.i << std::endl; + items.push_back(head.f); + aggregate(std::forward(args)...); + } + +public: + std::vector items; + Bar() = default; + Bar(const Foo &foo) { + items.reserve(1); + items.push_back(foo); + } + Bar(const Bar &other) : items(other.items) { + std::cout << "copy Bar" << std::endl; + } + //Bar(std::initializer_list args) : items(args) {} + + Bar& operator=(const Bar& other) { + std::cout << "assignment Bar" << std::endl; + // Check for self-assignment + if (this != &other) { + items = other.items; + //auto dummy = other.items; + //other.items; + } + return *this; + } + + template::value, void>> + Bar(const Args&... args) { + items.reserve(sizeof...(Args)); + std::cout << "create Bar from Baz" << std::endl; + aggregate(args...); + std::cout << "done" << std::endl; + } +}; + +int main() +{ + Bar bar; + { + Foo foo(5); + Bar dummy(foo); // creates 1 copy of foo + std::cout << dummy.items[0].i << std::endl; + bar = Bar(foo); // creates 1 copy to construct bar, 1 copy when assigning + } + std::cout << bar.items[0].i << std::endl; + //std::cout << foo.i << std::endl; + + Baz op1(Foo(1)); + Baz op2(Foo(2)); + + Bar bar2(op1, op2); + std::cout << bar2.items[0].i << " " << bar2.items[1].i << std::endl; + + return 0; +} \ No newline at end of file From a637313a2db2223253e8da31e6ba06bcb95205ce Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 24 Jan 2025 11:29:23 +0000 Subject: [PATCH 058/311] more memory experiments Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/memory.cpp | 131 ++++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 8 deletions(-) diff --git a/runtime/cudaq/dynamics/memory.cpp b/runtime/cudaq/dynamics/memory.cpp index ffb0bfeaf2..36c713a114 100644 --- a/runtime/cudaq/dynamics/memory.cpp +++ b/runtime/cudaq/dynamics/memory.cpp @@ -13,9 +13,6 @@ #include #include // enable_if, conjuction -template -using are_same = std::conjunction...>; - class Foo { public: int i; @@ -24,12 +21,38 @@ class Foo { Foo(const Foo &other) : i(other.i) { std::cout << "copy Foo" << std::endl; } + + Foo(Foo &&other) { + *this = std::forward(other); + } + + Foo& operator=(const Foo& other) { + std::cout << "assignment Foo" << std::endl; + // Check for self-assignment + if (this != &other) { + i = other.i; + } + return *this; + } + + Foo& operator=(Foo&& other) noexcept { + std::cout << "move Foo" << std::endl; + this->i = other.i; + return *this; + } }; class Baz { public: Foo f; - Baz(const Foo &foo) : f(foo) {} + Baz(Foo&& foo) { + std::cout << "create Baz" << std::endl; + f = std::move(foo); + } + + Baz(const Foo &foo) : f(foo) { + std::cout << "create Baz" << std::endl; + } Baz(const Baz& other) : f(other.f) { std::cout << "copy Baz" << std::endl; @@ -55,27 +78,42 @@ class Bar { public: std::vector items; Bar() = default; + + Bar(Foo&& foo) { + std::cout << "create Bar from &&" << std::endl; + items.push_back(std::forward(foo)); + } + + Bar(Bar&& other) { + items = std::move(other.items); + } + Bar(const Foo &foo) { - items.reserve(1); items.push_back(foo); } + Bar(const Bar &other) : items(other.items) { std::cout << "copy Bar" << std::endl; } + //Bar(std::initializer_list args) : items(args) {} + Bar& operator=(Bar&& other) noexcept { + std::cout << "move Bar" << std::endl; + this->items = std::forward>(other.items); + return *this; + } + Bar& operator=(const Bar& other) { std::cout << "assignment Bar" << std::endl; // Check for self-assignment if (this != &other) { items = other.items; - //auto dummy = other.items; - //other.items; } return *this; } - template::value, void>> + template...>::value, void>> Bar(const Args&... args) { items.reserve(sizeof...(Args)); std::cout << "create Bar from Baz" << std::endl; @@ -84,6 +122,41 @@ class Bar { } }; +class Dummy1 { +protected: + std::vector> terms; + Dummy1() = default; +public: + Dummy1(std::vector> data) : terms(data) {} + + std::vector get(int i) { + return terms[i]; + } +}; + +class Dummy2 : public Dummy1 { +public: + Dummy2(std::vector data) : Dummy1({data}) {} + + std::string get(int i) { + return terms[0][i]; + } +}; + +class Dummy3 { +public: + std::vector> items; + Dummy3(const std::vector& ops) { + std::cout << "construct Dummy3" << std::endl; + items.push_back(ops); + } + + Dummy3(std::vector&& ops) { + std::cout << "construct Dummy3" << std::endl; + items.push_back(std::move(ops)); + } +}; + int main() { Bar bar; @@ -102,5 +175,47 @@ int main() Bar bar2(op1, op2); std::cout << bar2.items[0].i << " " << bar2.items[1].i << std::endl; + // FIXME: AVOID FOO COPY HERE + Bar bar3(Baz(Foo(3)), Baz(Foo(4))); + std::cout << bar3.items[0].i << " " << bar3.items[1].i << std::endl; + + //Bar(1, Dummy()); + + std::vector data1 = {"op1", "op2"}; + std::vector data2 = {"op3", "op4"}; + + Dummy1 d1({data1, data2}); + Dummy2 d2(data1); + + std::cout << d2.get(0) << " " << d2.get(1) << std::endl; + std::cout << ((Dummy1)d2).get(0)[0] << " " << ((Dummy1)d2).get(0)[1] << std::endl; + + std::cout << d1.get(0)[0] << " " << d1.get(0)[1] << std::endl; + std::cout << d1.get(1)[0] << " " << d1.get(1)[1] << std::endl; + + std::vector ops = {}; + ops.reserve(2); + { + ops.push_back(Bar(Foo(9))); + Bar op(Foo(10)); + ops.push_back(op); + } + + Dummy3 d3(std::move(ops)); + std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; + //std::cout << ops[0].items[0].i << std::endl; + + { + std::cout << std::endl; + // an initializer list will always make an extra copy (by design of the initializer list) + // d3 = Dummy3({Foo(11), Foo(12)}); -> this will make an extra copy... + std::vector ops2 = {}; + ops2.reserve(2); + ops2.push_back(Foo(11)); + ops2.push_back(Foo(12)); + d3 = Dummy3(std::move(ops2)); + } + std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; + return 0; } \ No newline at end of file From 0aa6e8584c020dc95e8256faffa65b0794c6766f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 24 Jan 2025 14:18:27 +0000 Subject: [PATCH 059/311] revised the data structures - all tests now pass again Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 34 +-- runtime/cudaq/dynamics/instantiations.cpp | 68 +++-- runtime/cudaq/dynamics/product_operators.cpp | 2 +- runtime/cudaq/dynamics/scalar_operators.cpp | 53 ++-- runtime/cudaq/operators.h | 254 ++++++++++++++---- unittests/dynamics/scalar_ops_arithmetic.cpp | 2 +- 6 files changed, 280 insertions(+), 133 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 3f3805539c..e07bd900d1 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -15,22 +15,22 @@ namespace cudaq { +std::map elementary_operator::m_ops = {}; + elementary_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - int degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); // Build up the identity matrix. for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = 1.0 + 0.0 * 'j'; + mat[{i, i}] = 1.0 + 0.0j; } std::cout << "dumping the complex mat: \n"; @@ -292,14 +292,14 @@ operator_sum operator+(double other, const elementary_opera self}; std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum(product_operator(_other), product_operator(_self)); } operator_sum operator-(double other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); + return operator_sum(product_operator(_other), (-1. * self)); } product_operator operator*(std::complex other, const elementary_operator &self) { @@ -315,12 +315,12 @@ operator_sum operator+(std::complex other, const el self}; std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum(product_operator(_other), product_operator(_self)); } operator_sum operator-(std::complex other, const elementary_operator &self) { std::vector> _other = {scalar_operator(other)}; - return operator_sum({product_operator(_other), (-1. * self)}); + return operator_sum(product_operator(_other), (-1. * self)); } product_operator operator*(const scalar_operator &other, const elementary_operator &self) { @@ -330,12 +330,12 @@ product_operator operator*(const scalar_operator &other, co operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { std::vector> _other = {other}; std::vector> _self = {self}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum(product_operator(_other), product_operator(_self)); } operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { std::vector> _other = {other}; - return operator_sum({product_operator(_other), -1. * self}); + return operator_sum(product_operator(_other), -1. * self); } // right-hand arithmetics @@ -370,7 +370,7 @@ operator_sum elementary_operator::operator+(std::complex> _other = { other_scalar}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } operator_sum elementary_operator::operator-(std::complex other) const { @@ -381,7 +381,7 @@ operator_sum elementary_operator::operator-(std::complex> _other = { other_scalar}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } product_operator elementary_operator::operator*(const scalar_operator &other) const { @@ -397,7 +397,7 @@ operator_sum elementary_operator::operator+(const scalar_op *this}; std::vector> _other = { other}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } operator_sum elementary_operator::operator-(const scalar_operator &other) const { @@ -407,7 +407,7 @@ operator_sum elementary_operator::operator-(const scalar_op *this}; std::vector> _other = { -1. * other}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } product_operator elementary_operator::operator*(const elementary_operator &other) const { @@ -421,13 +421,13 @@ operator_sum elementary_operator::operator+(const elementar *this}; std::vector> _other = { other}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } operator_sum elementary_operator::operator-(const elementary_operator &other) const { std::vector> _this = { *this}; - return operator_sum({product_operator(_this), (-1. * other)}); + return operator_sum(product_operator(_this), (-1. * other)); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/instantiations.cpp index 71add82c27..52138d3ce3 100644 --- a/runtime/cudaq/dynamics/instantiations.cpp +++ b/runtime/cudaq/dynamics/instantiations.cpp @@ -32,13 +32,13 @@ product_operator operator*(const scalar_operator &other, const produc template requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); + return operator_sum(product_operator({other}), self); } template requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self * (-1.)}); + return operator_sum(product_operator({other}), self * (-1.)); } template @@ -50,7 +50,7 @@ product_operator operator*(double other, const product_operator requires std::derived_from operator_sum operator+(double other, const product_operator &self) { - return operator_sum({product_operator({scalar_operator(other)}), self}); + return operator_sum(product_operator({scalar_operator(other)}), self); } template @@ -68,7 +68,7 @@ product_operator operator*(const std::complex other, const pr template requires std::derived_from operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum({product_operator({scalar_operator(other)}), self}); + return operator_sum(product_operator({scalar_operator(other)}), self); } template @@ -90,7 +90,7 @@ product_operator operator*(const HandlerTy &other, const product_oper template requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); + return operator_sum(product_operator(other), self); } template @@ -273,7 +273,7 @@ operator_sum& operator_sum::operator-=(std::complex requires std::derived_from operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { term *= other; } @@ -283,9 +283,8 @@ operator_sum operator_sum::operator*(const scalar_operator template requires std::derived_from operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; + std::vector> combined_terms = this->get_terms(); + std::vector> _other = {other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } @@ -320,7 +319,7 @@ operator_sum& operator_sum::operator-=(const scalar_operat template requires std::derived_from operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { term *= other; } @@ -330,7 +329,7 @@ operator_sum operator_sum::operator*(const HandlerTy &othe template requires std::derived_from operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); std::vector> _other = { other}; combined_terms.push_back(product_operator(_other)); @@ -340,7 +339,7 @@ operator_sum operator_sum::operator+(const HandlerTy &othe template requires std::derived_from operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other * (-1.)); return operator_sum(combined_terms); } @@ -355,8 +354,7 @@ operator_sum& operator_sum::operator*=(const HandlerTy &ot template requires std::derived_from operator_sum& operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = { - other}; + std::vector> _other = {other}; *this = *this + product_operator(_other); return *this; } @@ -364,8 +362,7 @@ operator_sum& operator_sum::operator+=(const HandlerTy &ot template requires std::derived_from operator_sum& operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = { - other}; + std::vector> _other = {other}; *this = *this - product_operator(_other); return *this; } @@ -373,7 +370,7 @@ operator_sum& operator_sum::operator-=(const HandlerTy &ot template requires std::derived_from operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { term *= other; } @@ -383,7 +380,7 @@ operator_sum operator_sum::operator*(const product_operato template requires std::derived_from operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other); return operator_sum(combined_terms); } @@ -391,7 +388,7 @@ operator_sum operator_sum::operator+(const product_operato template requires std::derived_from operator_sum operator_sum::operator-(const product_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other * (-1.)); return operator_sum(combined_terms); } @@ -420,7 +417,7 @@ operator_sum& operator_sum::operator-=(const product_opera template requires std::derived_from operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = terms; + auto self_terms = this->get_terms(); std::vector> product_terms; auto other_terms = other.get_terms(); for (auto &term : self_terms) { @@ -434,10 +431,9 @@ operator_sum operator_sum::operator*(const operator_sum requires std::derived_from operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = terms; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.terms.begin()), - std::make_move_iterator(other.terms.end())); + std::vector> combined_terms = this->get_terms(); + // make_move_iterator for && overload + combined_terms.insert(combined_terms.end(), other.terms.begin(), other.terms.end()); return operator_sum(combined_terms); } @@ -525,7 +521,7 @@ template requires std::derived_from product_operator product_operator::operator*(const scalar_operator &other) const { std::vector> - combined_terms = ops; + combined_terms = operator_sum::terms[0]; combined_terms.push_back(other); return product_operator(combined_terms); } @@ -535,13 +531,14 @@ requires std::derived_from operator_sum product_operator::operator+(const scalar_operator &other) const { std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum(*this, product_operator(_other)); } template requires std::derived_from operator_sum product_operator::operator-(const scalar_operator &other) const { - return operator_sum({*this, product_operator({other * (-1.)})}); + std::vector> minus_term = {other * (-1.)}; + return operator_sum(*this, product_operator(minus_term)); } template @@ -555,7 +552,7 @@ template requires std::derived_from product_operator product_operator::operator*(const HandlerTy &other) const { std::vector> - combined_terms = ops; + combined_terms = operator_sum::terms[0]; combined_terms.push_back(other); return product_operator(combined_terms); } @@ -565,13 +562,13 @@ requires std::derived_from operator_sum product_operator::operator+(const HandlerTy &other) const { std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum(*this, product_operator(_other)); } template requires std::derived_from operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum({*this, other * (-1.)}); + return operator_sum(*this, other * (-1.)); } template @@ -585,24 +582,21 @@ template requires std::derived_from product_operator product_operator::operator*(const product_operator &other) const { std::vector> - combined_terms = ops; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.ops.begin()), - std::make_move_iterator(other.ops.end())); + combined_terms = operator_sum::terms[0]; + combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); return product_operator(combined_terms); } template requires std::derived_from operator_sum product_operator::operator+(const product_operator &other) const { - return operator_sum({*this, other}); + return operator_sum(*this, other); } template requires std::derived_from operator_sum product_operator::operator-(const product_operator &other) const { - std::vector> combined_terms = {*this, other * (-1.)}; - return operator_sum(combined_terms); + return operator_sum(*this, other * (-1.)); } template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 1e9205c592..39a9e5d03f 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -149,7 +149,7 @@ std::vector product_operator::degrees() const { // The variant type makes it difficult auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; auto endFunc = [](auto &&t) { return t.degrees.end(); }; - for (const auto &term : ops) { + for (const auto &term : operator_sum::terms[0]) { unique_degrees.insert(std::visit(beginFunc, term), std::visit(endFunc, term)); } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 7eacac4775..c03e1f377b 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,32 +14,12 @@ namespace cudaq { -// constructors - -/// @brief Constructor that just takes and returns a complex double value. -scalar_operator::scalar_operator(std::complex value) { - m_constant_value = value; - auto func = [&](std::map> _none) { - return m_constant_value; - }; - generator = ScalarCallbackFunction(func); -} - -/// @brief Constructor that just takes a double and returns a complex double. -scalar_operator::scalar_operator(double value) { - std::complex castValue(value, 0.0); - m_constant_value = castValue; - auto func = [&](std::map> _none) { - return m_constant_value; - }; - generator = ScalarCallbackFunction(func); -} - // evaluations std::complex scalar_operator::evaluate( const std::map> parameters) const { - return generator(parameters); + if (m_constant_value.has_value()) return m_constant_value.value(); + else return generator(parameters); } matrix_2 scalar_operator::to_matrix( @@ -58,7 +38,7 @@ matrix_2 scalar_operator::to_matrix( [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); @@ -73,7 +53,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); @@ -89,7 +69,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_DOUBLES(*); @@ -99,6 +79,10 @@ ARITHMETIC_OPERATIONS_DOUBLES(-); #define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op(double other) { \ + if (this->m_constant_value.has_value()) { \ + this->m_constant_value.value() op other; \ + return *this; \ + } \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ @@ -106,7 +90,7 @@ ARITHMETIC_OPERATIONS_DOUBLES(-); [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ - this->generator = ScalarCallbackFunction(newGenerator); \ + this->generator = newGenerator; \ return *this; \ } @@ -122,7 +106,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); @@ -133,6 +117,10 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op( \ std::complex other) { \ + if (this->m_constant_value.has_value()) { \ + this->m_constant_value.value() op other; \ + return *this; \ + } \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ @@ -140,7 +128,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ - this->generator = ScalarCallbackFunction(newGenerator); \ + this->generator = newGenerator; \ return *this; \ } @@ -156,7 +144,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_SCALAR_OPS(*); @@ -167,6 +155,11 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op( \ const scalar_operator &other) { \ + if (this->m_constant_value.has_value() && \ + other.m_constant_value.has_value()) { \ + this->m_constant_value.value() op other.m_constant_value.value(); \ + return *this; \ + } \ /* Need to move the existing generating function to a new operator so \ * that we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ @@ -174,7 +167,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ }; \ - this->generator = ScalarCallbackFunction(newGenerator); \ + this->generator = newGenerator; \ return *this; \ } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d5928ff743..7e98629fd4 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace cudaq { @@ -169,21 +170,74 @@ requires std::derived_from class operator_sum { private: - std::vector> terms; - std::vector> canonicalize_product(product_operator &prod) const; std::vector> _canonical_terms() const; + void aggregate_terms(const product_operator& head) { + terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + } + + template + void aggregate_terms(const product_operator &head, Args&& ... args) { + terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + aggregate_terms(std::forward(args)...); + } + +protected: + operator_sum() = default; + std::vector>> terms; + public: + /// @brief Construct a `cudaq::operator_sum` given a sequence of /// `cudaq::product_operator`'s. /// This operator expression represents a sum of terms, where each term /// is a product of elementary and scalar operators. - operator_sum(const std::vector> &terms) - : terms(terms) {} + template, Args>...>::value, void>> + operator_sum(const Args&... args) { + std::cout << "op sum constructor" << std::endl; + terms.reserve(sizeof...(Args)); + aggregate_terms(args...); + } + + operator_sum(const std::vector>& terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(term.terms[0]); + } + } + + operator_sum(std::vector>&& terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(std::move(term.terms[0])); + } + } + + // copy constructor + operator_sum(const operator_sum &other) + : terms(other.terms) {} + + // move constructor + operator_sum(operator_sum &&other) + : terms(std::move(other.terms)) {} + + // assignment operator + operator_sum& operator=(const operator_sum& other) { + if (this != &other) { + terms = other.terms; + } + return *this; + } + + // move assignment operator + operator_sum& operator=(operator_sum &&other) { + terms = std::move(other.terms); + return *this; + } ~operator_sum() = default; @@ -296,7 +350,13 @@ class operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() const { return terms; } + std::vector> get_terms() const { + std::vector> prods; + prods.reserve(terms.size()); + for (auto term : terms) { + prods.push_back(product_operator(term)); + } + return prods; } }; /// @brief Represents an operator expression consisting of a product of @@ -308,17 +368,64 @@ requires std::derived_from class product_operator : public operator_sum { private: - std::vector> ops; + + void aggregate_terms(const HandlerTy& head) { + operator_sum::terms[0].push_back(head); + } + + template + void aggregate_terms(const HandlerTy &head, Args&& ... args) { + operator_sum::terms[0].push_back(head); + aggregate_terms(std::forward(args)...); + } public: // Constructor for an operator expression that represents a product // of scalar and elementary operators. // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. - product_operator( - std::vector> - atomic_operators) - : operator_sum({*this}), ops(atomic_operators) {} + template...>::value, void>> + product_operator(const Args&... args) { + std::cout << "prod op constructor" << std::endl; + std::vector> ops = {}; + ops.reserve(sizeof...(Args)); + operator_sum::terms.push_back(ops); + aggregate_terms(args...); + } + + product_operator(const std::vector>& atomic_operators) { + std::cout << "prod op constructor" << std::endl; + operator_sum::terms.push_back(atomic_operators); + } + + product_operator(std::vector>&& atomic_operators) { + std::cout << "prod op constructor" << std::endl; + operator_sum::terms.push_back(std::move(atomic_operators)); + } + + // copy constructor + product_operator(const product_operator &other) { + operator_sum::terms = other.terms; + } + + // move constructor + product_operator(product_operator &&other) { + operator_sum::terms = std::move(other.terms); + } + + // assignment operator + product_operator& operator=(const product_operator& other) { + if (this != &other) { + operator_sum::terms = other.terms; + } + return *this; + } + + // move assignment operator + product_operator& operator=(product_operator &&other) { + operator_sum::terms = std::move(other.terms); + return *this; + } ~product_operator() = default; @@ -328,12 +435,11 @@ class product_operator : public operator_sum { /// @brief Return the number of operator terms that make up this product /// operator. - int term_count() const { return ops.size(); } + int term_count() const { return operator_sum::terms[0].size(); } - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: + /// FIXME: ELIMINATE THIS std::vector> get_operators() const { - return ops; + return operator_sum::terms[0]; }; /// @brief Return the `product_operator` as a string. @@ -425,21 +531,57 @@ class scalar_operator { private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. - std::complex m_constant_value; + std::optional> m_constant_value; + + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; public: + scalar_operator(double value) + : m_constant_value(value), generator() {} + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value) + : m_constant_value(value), generator() {} + + + scalar_operator(const ScalarCallbackFunction &create) + : m_constant_value(), generator(create) {} + /// @brief Constructor that just takes a callback function with no /// arguments. + scalar_operator(ScalarCallbackFunction &&create) + : m_constant_value() { + generator = std::move(create); + } - scalar_operator(ScalarCallbackFunction &&create) { - generator = ScalarCallbackFunction(create); + // copy constructor + scalar_operator(const scalar_operator &other) + : m_constant_value(other.m_constant_value), generator(other.generator) {} + + // move constructor + scalar_operator(scalar_operator &&other) + : m_constant_value(other.m_constant_value) { + generator = std::move(other.generator); } - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value); - scalar_operator(double value); + // assignment operator + scalar_operator& operator=(const scalar_operator &other) { + m_constant_value = other.m_constant_value; + generator = other.generator; + return *this; + } + + // move assignment operator + scalar_operator& operator=(scalar_operator &&other) { + m_constant_value = other.m_constant_value; + generator = std::move(other.generator); + return *this; + } /// NOTE: We should revisit these constructors and remove any that have /// become unnecessary as the implementation improves. @@ -450,11 +592,6 @@ class scalar_operator { ~scalar_operator() = default; - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - // Need this property for consistency with other inherited types. // Particularly, to be used when the scalar operator is held within // a variant type next to elementary operators. @@ -511,10 +648,18 @@ class scalar_operator { }; -class elementary_operator : public product_operator { +class elementary_operator { private: - std::map m_ops; + static std::map m_ops; + +protected: + // FIXME: revise implementation + /// @brief The number of levels, that is the dimension, for each degree of + /// freedom in canonical order that the operator acts on. A value of zero or + /// less indicates that the operator is defined for any dimension of that + /// degree. + std::map expected_dimensions; public: // The constructor should never be called directly by the user: @@ -523,28 +668,43 @@ class elementary_operator : public product_operator { /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - elementary_operator(std::string operator_id, std::vector degrees) - : id(operator_id), degrees(degrees), - product_operator({std::variant{*this}}) { } + elementary_operator(std::string operator_id, const std::vector °rees) + : id(operator_id), degrees(degrees) { + std::cout << "elem op constructor" << std::endl; + } + + // constructor + elementary_operator(std::string operator_id, std::vector &°rees) + : id(operator_id), degrees(std::move(degrees)) { + std::cout << "elem op constructor" << std::endl; + } - // Copy constructor. FIXME: NEEDED? + // copy constructor elementary_operator(const elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id), - product_operator({std::variant{*this}}) { } + : degrees(other.degrees), id(other.id) {} - elementary_operator(elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id), - product_operator({std::variant{*this}}) { } + // move constructor + elementary_operator(elementary_operator &&other) + : degrees(std::move(other.degrees)), id(other.id) {} + + // assignment operator + elementary_operator& operator=(const elementary_operator& other) { + if (this != &other) { + degrees = other.degrees; + id = other.id; + } + return *this; + } + + // move assignment operator + elementary_operator& operator=(elementary_operator &&other) { + degrees = std::move(other.degrees); + id = other.id; + return *this; + } ~elementary_operator() = default; - /// @brief The number of levels, that is the dimension, for each degree of - /// freedom in canonical order that the operator acts on. A value of zero or - /// less indicates that the operator is defined for any dimension of that - /// degree. - std::map expected_dimensions; /// @brief The degrees of freedom that the operator acts on in canonical /// order. std::vector degrees; @@ -634,13 +794,13 @@ class elementary_operator : public product_operator { template void define(std::string operator_id, std::map expected_dimensions, Func create) { - if (m_ops.find(operator_id) != m_ops.end()) { + if (elementary_operator::m_ops.find(operator_id) != elementary_operator::m_ops.end()) { // todo: make a nice error message to say op already exists throw; } auto defn = Definition(); defn.create_definition(operator_id, expected_dimensions, create); - m_ops[operator_id] = defn; + elementary_operator::m_ops[operator_id] = defn; } }; diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index 00f93c1fc8..b6d237eba4 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { +TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { // Arithmetic overloads against complex doubles. std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; From 8cb682e41ef6c353944c5d8c4e0903df6ad50a85 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 27 Jan 2025 17:34:56 +0000 Subject: [PATCH 060/311] store one coefficient per product only Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 50 +- .../cudaq/dynamics/elementary_operators.cpp | 149 ++---- runtime/cudaq/dynamics/instantiations.cpp | 95 ++-- runtime/cudaq/dynamics/product_operators.cpp | 14 +- runtime/cudaq/dynamics/scalar_operators.cpp | 6 +- .../cudaq/dynamics/template_declarations.h | 154 ++++++ runtime/cudaq/operators.h | 455 +++++++----------- .../dynamics/elementary_ops_arithmetic.cpp | 19 +- unittests/dynamics/operator_sum.cpp | 34 +- .../dynamics/product_operators_arithmetic.cpp | 42 +- 10 files changed, 509 insertions(+), 509 deletions(-) create mode 100644 runtime/cudaq/dynamics/template_declarations.h diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index d5013ffc9c..d2760841ec 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -47,13 +47,30 @@ class CallbackFunction { _callback_func = std::forward(callable); } - // Copy constructor. - CallbackFunction(CallbackFunction &other) { + // copy constructor + CallbackFunction(const CallbackFunction &other) { _callback_func = other._callback_func; } - CallbackFunction(const CallbackFunction &other) { - _callback_func = other._callback_func; + // move constructor. + CallbackFunction(CallbackFunction &&other) { + _callback_func = std::move(other._callback_func); + } + + // assignment operator + CallbackFunction& operator=(const CallbackFunction &other) { + if (this != &other) { + _callback_func = other._callback_func; + } + return *this; + } + + // move assignment operator + CallbackFunction& operator=(CallbackFunction &&other) { + if (this != &other) { + _callback_func = std::move(other._callback_func); + } + return *this; } matrix_2 @@ -86,13 +103,30 @@ class ScalarCallbackFunction : CallbackFunction { _callback_func = std::forward(callable); } - // Copy constructor. - ScalarCallbackFunction(ScalarCallbackFunction &other) { + // copy constructor + ScalarCallbackFunction(const ScalarCallbackFunction &other) { _callback_func = other._callback_func; } - ScalarCallbackFunction(const ScalarCallbackFunction &other) { - _callback_func = other._callback_func; + // move constructor. + ScalarCallbackFunction(ScalarCallbackFunction &&other) { + _callback_func = std::move(other._callback_func); + } + + // assignment operator + ScalarCallbackFunction& operator=(const ScalarCallbackFunction &other) { + if (this != &other) { + _callback_func = other._callback_func; + } + return *this; + } + + // move assignment operator + ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other) { + if (this != &other) { + _callback_func = std::move(other._callback_func); + } + return *this; } bool operator!() { return (!_callback_func); } diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index e07bd900d1..b9b32e4682 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -32,10 +32,6 @@ elementary_operator elementary_operator::identity(int degree) { for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = 1.0 + 0.0j; } - - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -58,9 +54,6 @@ elementary_operator elementary_operator::zero(int degree) { auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -83,9 +76,6 @@ elementary_operator elementary_operator::annihilate(int degree) { for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -108,9 +98,6 @@ elementary_operator elementary_operator::create(int degree) { for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -137,9 +124,6 @@ elementary_operator elementary_operator::position(int degree) { mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -166,9 +150,6 @@ elementary_operator elementary_operator::momentum(int degree) { mat[{i, i + 1}] = -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -191,9 +172,6 @@ elementary_operator elementary_operator::number(int degree) { for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = static_cast(i) + 0.0j; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -216,9 +194,6 @@ elementary_operator elementary_operator::parity(int degree) { for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -253,9 +228,6 @@ elementary_operator::displace(int degree, std::complex amplitude) { // // requires copies here. Maybe we can just use eigen directly here // // to limit to one copy, but we can address that later. // auto mat = temp_mat.exp(); - // std::cout << "dumping the complex mat: \n"; - // mat.dump(); - // std::cout << "\ndone\n\n"; // return mat; // }; // op.define(op_id, op.expected_dimensions, func); @@ -280,154 +252,103 @@ matrix_2 elementary_operator::to_matrix( // left-hand arithmetics product_operator operator*(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); + return product_operator(other, self); } operator_sum operator+(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_other), product_operator(_self)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., self)); } operator_sum operator-(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_other), (-1. * self)); + product_operator coefficient(other); + return operator_sum(coefficient, -1. * self); } product_operator operator*(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); + return product_operator(other, self); } operator_sum operator+(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_other), product_operator(_self)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., self)); } operator_sum operator-(std::complex other, const elementary_operator &self) { - std::vector> _other = {scalar_operator(other)}; - return operator_sum(product_operator(_other), (-1. * self)); + product_operator coefficient(other); + return operator_sum(coefficient, -1. * self); } product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator({other, self}); + return product_operator(other, self); } operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - std::vector> _self = {self}; - return operator_sum(product_operator(_other), product_operator(_self)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., self)); } operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - return operator_sum(product_operator(_other), -1. * self); + product_operator coefficient(other); + return operator_sum(coefficient, -1. * self); } // right-hand arithmetics product_operator elementary_operator::operator*(double other) const { - std::complex value(other, 0.0); - return *this * value; + return product_operator(other, *this); } operator_sum elementary_operator::operator+(double other) const { - std::complex value(other, 0.0); - return *this + value; + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., *this)); } operator_sum elementary_operator::operator-(double other) const { - std::complex value(other, 0.0); - return *this - value; + product_operator coefficient(-1. * other); + return operator_sum(coefficient, product_operator(1., *this)); } product_operator elementary_operator::operator*(std::complex other) const { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - *this, other_scalar}; - return product_operator(_args); + return product_operator(other, *this); } operator_sum elementary_operator::operator+(std::complex other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator(other); - std::vector> _this = { - *this}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., *this)); } operator_sum elementary_operator::operator-(std::complex other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator((-1. * other)); - std::vector> _this = { - *this}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(-1. * other); + return operator_sum(coefficient, product_operator(1., *this)); } product_operator elementary_operator::operator*(const scalar_operator &other) const { - std::vector> _args = { - *this, other}; - return product_operator(_args); + return product_operator(other, *this); } operator_sum elementary_operator::operator+(const scalar_operator &other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - std::vector> _this = { - *this}; - std::vector> _other = { - other}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., *this)); } operator_sum elementary_operator::operator-(const scalar_operator &other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - std::vector> _this = { - *this}; - std::vector> _other = { - -1. * other}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(-1. * other); + return operator_sum(coefficient, product_operator(1., *this)); } product_operator elementary_operator::operator*(const elementary_operator &other) const { - std::vector> _args = { - *this, other}; - return product_operator(_args); + return product_operator(1., *this, other); } operator_sum elementary_operator::operator+(const elementary_operator &other) const { - std::vector> _this = { - *this}; - std::vector> _other = { - other}; - return operator_sum(product_operator(_this), product_operator(_other)); + auto term1 = product_operator(1., *this); + auto term2 = product_operator(1., other); + return operator_sum(term1, term2); } operator_sum elementary_operator::operator-(const elementary_operator &other) const { - std::vector> _this = { - *this}; - return operator_sum(product_operator(_this), (-1. * other)); + return operator_sum(product_operator(1., *this), -1. * other); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/instantiations.cpp index 52138d3ce3..77741ac1a9 100644 --- a/runtime/cudaq/dynamics/instantiations.cpp +++ b/runtime/cudaq/dynamics/instantiations.cpp @@ -22,11 +22,7 @@ namespace cudaq { template requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this scalar operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); + return product_operator(other * std::move(self.get_coefficient()), std::move(self.get_terms())); } template @@ -80,17 +76,15 @@ operator_sum operator-(const std::complex other, const produc template requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this elementary operator to the front of the terms list. + std::vector terms = std::move(self.get_terms()); terms.insert(terms.begin(), other); - return product_operator(terms); + return product_operator(std::move(self.get_coefficient()), std::move(terms)); } template requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(other), self); + return operator_sum(product_operator(1., other), self); } template @@ -114,7 +108,7 @@ template requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self) { std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), product_operator({other})); + terms.insert(terms.begin(), product_operator(other)); return operator_sum(terms); } @@ -123,7 +117,7 @@ requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self) { auto negative_self = self * (-1.); std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), product_operator({other})); + terms.insert(terms.begin(), product_operator(other)); return operator_sum(terms); } @@ -166,28 +160,19 @@ operator_sum operator-(std::complex other, const operator_sum template requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * self; + return ((operator_sum)product_operator(1., other)) * self; } template requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + self; + return ((operator_sum)product_operator(1., other)) + self; } template requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - self; + return ((operator_sum)product_operator(1., other)) - self; } // operator sum right-hand arithmetics @@ -273,7 +258,7 @@ operator_sum& operator_sum::operator-=(std::complex requires std::derived_from operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = this->get_terms(); + std::vector> combined_terms = std::move(this->get_terms()); for (auto &term : combined_terms) { term *= other; } @@ -283,9 +268,9 @@ operator_sum operator_sum::operator*(const scalar_operator template requires std::derived_from operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = this->get_terms(); - std::vector> _other = {other}; - combined_terms.push_back(product_operator(_other)); + // FIXME: reserve length + auto combined_terms = std::move(this->get_terms()); + combined_terms.push_back(product_operator(other)); return operator_sum(combined_terms); } @@ -329,18 +314,16 @@ operator_sum operator_sum::operator*(const HandlerTy &othe template requires std::derived_from operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = this->get_terms(); - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); + auto combined_terms = std::move(this->get_terms()); + combined_terms.push_back(product_operator(1., other)); return operator_sum(combined_terms); } template requires std::derived_from operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = this->get_terms(); - combined_terms.push_back(other * (-1.)); + std::vector> combined_terms = std::move(this->get_terms()); + combined_terms.push_back(product_operator(-1., other)); return operator_sum(combined_terms); } @@ -354,16 +337,16 @@ operator_sum& operator_sum::operator*=(const HandlerTy &ot template requires std::derived_from operator_sum& operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = {other}; - *this = *this + product_operator(_other); + this->coefficients.push_back(1.); + this->terms.push_back({other}); return *this; } template requires std::derived_from operator_sum& operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = {other}; - *this = *this - product_operator(_other); + this->coefficients.push_back(-1.); + this->terms.push_back({other}); return *this; } @@ -431,9 +414,9 @@ operator_sum operator_sum::operator*(const operator_sum requires std::derived_from operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = this->get_terms(); - // make_move_iterator for && overload - combined_terms.insert(combined_terms.end(), other.terms.begin(), other.terms.end()); + std::vector> combined_terms = std::move(this->get_terms()); + std::vector> other_terms = std::move(other.get_terms()); + combined_terms.insert(combined_terms.end(), std::make_move_iterator(other_terms.begin()), std::make_move_iterator(other_terms.end())); return operator_sum(combined_terms); } @@ -520,25 +503,21 @@ product_operator& product_operator::operator*=(std::comple template requires std::derived_from product_operator product_operator::operator*(const scalar_operator &other) const { - std::vector> - combined_terms = operator_sum::terms[0]; - combined_terms.push_back(other); - return product_operator(combined_terms); + return product_operator(this->coefficients[0] * other, this->terms[0]); } template requires std::derived_from operator_sum product_operator::operator+(const scalar_operator &other) const { - std::vector> _other = { - other}; - return operator_sum(*this, product_operator(_other)); + product_operator coefficient(other); + return operator_sum(coefficient, *this); } template requires std::derived_from operator_sum product_operator::operator-(const scalar_operator &other) const { - std::vector> minus_term = {other * (-1.)}; - return operator_sum(*this, product_operator(minus_term)); + product_operator coefficient(-1. * other); + return operator_sum(coefficient, *this); } template @@ -551,24 +530,21 @@ product_operator& product_operator::operator*=(const scala template requires std::derived_from product_operator product_operator::operator*(const HandlerTy &other) const { - std::vector> - combined_terms = operator_sum::terms[0]; + auto combined_terms = this->terms[0]; combined_terms.push_back(other); - return product_operator(combined_terms); + return product_operator(1., combined_terms); } template requires std::derived_from operator_sum product_operator::operator+(const HandlerTy &other) const { - std::vector> _other = { - other}; - return operator_sum(*this, product_operator(_other)); + return operator_sum(*this, product_operator(1., other)); } template requires std::derived_from operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum(*this, other * (-1.)); + return operator_sum(*this, product_operator(-1., other)); } template @@ -581,10 +557,9 @@ product_operator& product_operator::operator*=(const Handl template requires std::derived_from product_operator product_operator::operator*(const product_operator &other) const { - std::vector> - combined_terms = operator_sum::terms[0]; + auto combined_terms = this->terms[0]; combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); - return product_operator(combined_terms); + return product_operator(1., combined_terms); } template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 39a9e5d03f..d8bb0dc5f1 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -146,18 +146,10 @@ matrix_2 product_operator::to_matrix( template std::vector product_operator::degrees() const { std::set unique_degrees; - // The variant type makes it difficult - auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; - auto endFunc = [](auto &&t) { return t.degrees.end(); }; - for (const auto &term : operator_sum::terms[0]) { - unique_degrees.insert(std::visit(beginFunc, term), - std::visit(endFunc, term)); - } - // Erase any `-1` degree values that may have come from scalar operators. - auto it = unique_degrees.find(-1); - if (it != unique_degrees.end()) { - unique_degrees.erase(it); + for (const HandlerTy &term : this->get_terms()) { + unique_degrees.insert(term.degrees.begin(), term.degrees.end()); } + // FIXME: SORT THE DEGREES return std::vector(unique_degrees.begin(), unique_degrees.end()); } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index c03e1f377b..2463910cbd 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -66,7 +66,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ scalar_operator scalar_operator::operator op(double other) const { \ auto newGenerator = \ - [=, this](std::map> parameters) { \ + [=, *this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(newGenerator); \ @@ -103,7 +103,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); scalar_operator scalar_operator::operator op( \ std::complex other) const{ \ auto newGenerator = \ - [=, this](std::map> parameters) { \ + [=, *this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(newGenerator); \ @@ -141,7 +141,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); scalar_operator scalar_operator::operator op( \ const scalar_operator &other) const { \ auto newGenerator = \ - [=, this](std::map> parameters) { \ + [=, *this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(newGenerator); \ diff --git a/runtime/cudaq/dynamics/template_declarations.h b/runtime/cudaq/dynamics/template_declarations.h new file mode 100644 index 0000000000..e1cec53736 --- /dev/null +++ b/runtime/cudaq/dynamics/template_declarations.h @@ -0,0 +1,154 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +//#include +#include +#include + +namespace cudaq { + +class scalar_operator; + +class elementary_operator; + +template +requires std::derived_from +class product_operator; + +template +requires std::derived_from +class operator_sum; + +template +requires std::derived_from +product_operator operator*(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(double other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const product_operator &self); + +template +requires std::derived_from +operator_sum operator*(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const operator_sum &self); + + +#ifndef CUDAQ_INSTANTIATE_TEMPLATES +extern template +product_operator operator*(double other, const product_operator &self); +extern template +operator_sum operator+(double other, const product_operator &self); +extern template +operator_sum operator-(double other, const product_operator &self); +extern template +product_operator operator*(std::complex other, const product_operator &self); +extern template +operator_sum operator+(std::complex other, const product_operator &self); +extern template +operator_sum operator-(std::complex other, const product_operator &self); +extern template +product_operator operator*(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +extern template +product_operator operator*(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + +extern template +operator_sum operator*(double other, const operator_sum &self); +extern template +operator_sum operator+(double other, const operator_sum &self); +extern template +operator_sum operator-(double other, const operator_sum &self); +extern template +operator_sum operator*(std::complex other, const operator_sum &self); +extern template +operator_sum operator+(std::complex other, const operator_sum &self); +extern template +operator_sum operator-(std::complex other, const operator_sum &self); +extern template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); +#endif + +} \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 7e98629fd4..7702242b6c 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,6 +8,7 @@ #pragma once +#include "dynamics/template_declarations.h" #include "definition.h" #include "utils/tensor.h" @@ -20,145 +21,130 @@ namespace cudaq { -class scalar_operator; +class scalar_operator { -class elementary_operator; +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::optional> m_constant_value; -template -requires std::derived_from -class product_operator; + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; -template -requires std::derived_from -class operator_sum; +public: + scalar_operator(double value) + : m_constant_value(value), generator() {} -template -requires std::derived_from -product_operator operator*(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(double other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const product_operator &self); + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value) + : m_constant_value(value), generator() {} -template -requires std::derived_from -operator_sum operator*(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const operator_sum &self); + scalar_operator(const ScalarCallbackFunction &create) + : m_constant_value(), generator(create) {} -#ifdef CUDAQ_INSTANTIATE_TEMPLATES -#else -extern template -product_operator operator*(double other, const product_operator &self); -extern template -operator_sum operator+(double other, const product_operator &self); -extern template -operator_sum operator-(double other, const product_operator &self); -extern template -product_operator operator*(std::complex other, const product_operator &self); -extern template -operator_sum operator+(std::complex other, const product_operator &self); -extern template -operator_sum operator-(std::complex other, const product_operator &self); -extern template -product_operator operator*(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -extern template -product_operator operator*(const elementary_operator &other, const product_operator &self); -extern template -operator_sum operator+(const elementary_operator &other, const product_operator &self); -extern template -operator_sum operator-(const elementary_operator &other, const product_operator &self); - -extern template -operator_sum operator*(double other, const operator_sum &self); -extern template -operator_sum operator+(double other, const operator_sum &self); -extern template -operator_sum operator-(double other, const operator_sum &self); -extern template -operator_sum operator*(std::complex other, const operator_sum &self); -extern template -operator_sum operator+(std::complex other, const operator_sum &self); -extern template -operator_sum operator-(std::complex other, const operator_sum &self); -extern template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); -#endif + /// @brief Constructor that just takes a callback function with no + /// arguments. + scalar_operator(ScalarCallbackFunction &&create) + : m_constant_value() { + generator = std::move(create); + } + + // copy constructor + scalar_operator(const scalar_operator &other) + : m_constant_value(other.m_constant_value), generator(other.generator) {} + + // move constructor + scalar_operator(scalar_operator &&other) + : m_constant_value(other.m_constant_value) { + generator = std::move(other.generator); + } + + // assignment operator + scalar_operator& operator=(const scalar_operator &other) { + if (this != &other) { + m_constant_value = other.m_constant_value; + generator = other.generator; + } + return *this; + } + + // move assignment operator + scalar_operator& operator=(scalar_operator &&other) { + if (this != &other) { + m_constant_value = other.m_constant_value; + generator = std::move(other.generator); + } + return *this; + } + + /// NOTE: We should revisit these constructors and remove any that have + /// become unnecessary as the implementation improves. + // scalar_operator() = default; + // Copy constructor. + // scalar_operator(const scalar_operator &other); + // scalar_operator(scalar_operator &other); + + ~scalar_operator() = default; + + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {}; + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(const std::map> parameters) const; + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatibility with the other inherited classes. + matrix_2 to_matrix(const std::map dimensions, + const std::map> parameters) const; + + // Arithmetic overloads against other operator types. + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + scalar_operator& operator*=(double other); + scalar_operator& operator/=(double other); + scalar_operator& operator+=(double other); + scalar_operator& operator-=(double other); + scalar_operator operator*(std::complex other) const; + scalar_operator operator/(std::complex other) const; + scalar_operator operator+(std::complex other) const; + scalar_operator operator-(std::complex other) const; + scalar_operator& operator*=(std::complex other); + scalar_operator& operator/=(std::complex other); + scalar_operator& operator+=(std::complex other); + scalar_operator& operator-=(std::complex other); + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; + scalar_operator& operator*=(const scalar_operator &other); + scalar_operator& operator/=(const scalar_operator &other); + scalar_operator& operator+=(const scalar_operator &other); + scalar_operator& operator-=(const scalar_operator &other); + /// TODO: implement and test pow + + friend scalar_operator operator*(double other, const scalar_operator &self); + friend scalar_operator operator/(double other, const scalar_operator &self); + friend scalar_operator operator+(double other, const scalar_operator &self); + friend scalar_operator operator-(double other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, const scalar_operator &self); + friend scalar_operator operator/(std::complex other, const scalar_operator &self); + friend scalar_operator operator+(std::complex other, const scalar_operator &self); + friend scalar_operator operator-(std::complex other, const scalar_operator &self); + + // /// @brief Returns true if other is a scalar operator with the same + // /// generator. + // bool operator==(scalar_operator other); +}; /// @brief Represents an operator expression consisting of a sum of terms, where @@ -177,18 +163,22 @@ class operator_sum { _canonical_terms() const; void aggregate_terms(const product_operator& head) { - terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + terms.push_back(head.terms[0]); + coefficients.push_back(head.coefficients[0]); } - + template void aggregate_terms(const product_operator &head, Args&& ... args) { - terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + terms.push_back(head.terms[0]); + coefficients.push_back(head.coefficients[0]); aggregate_terms(std::forward(args)...); } protected: + operator_sum() = default; - std::vector>> terms; + std::vector> terms; + std::vector coefficients; public: @@ -198,15 +188,17 @@ class operator_sum { /// is a product of elementary and scalar operators. template, Args>...>::value, void>> operator_sum(const Args&... args) { - std::cout << "op sum constructor" << std::endl; terms.reserve(sizeof...(Args)); + coefficients.reserve(sizeof...(Args)); aggregate_terms(args...); } operator_sum(const std::vector>& terms) { this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); for (const product_operator& term : terms) { this->terms.push_back(term.terms[0]); + this->coefficients.push_back(term.coefficients[0]); } } @@ -214,20 +206,22 @@ class operator_sum { this->terms.reserve(terms.size()); for (const product_operator& term : terms) { this->terms.push_back(std::move(term.terms[0])); + this->coefficients.push_back(std::move(term.coefficients[0])); } } // copy constructor operator_sum(const operator_sum &other) - : terms(other.terms) {} + : coefficients(other.coefficients), terms(other.terms) {} // move constructor operator_sum(operator_sum &&other) - : terms(std::move(other.terms)) {} + : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} // assignment operator operator_sum& operator=(const operator_sum& other) { if (this != &other) { + coefficients = other.coefficients; terms = other.terms; } return *this; @@ -235,7 +229,10 @@ class operator_sum { // move assignment operator operator_sum& operator=(operator_sum &&other) { - terms = std::move(other.terms); + if (this != &other) { + coefficients = std::move(other.coefficients); + terms = std::move(other.terms); + } return *this; } @@ -353,8 +350,8 @@ class operator_sum { std::vector> get_terms() const { std::vector> prods; prods.reserve(terms.size()); - for (auto term : terms) { - prods.push_back(product_operator(term)); + for (size_t i = 0; i < terms.size(); ++i) { + prods.push_back(product_operator(coefficients[i], terms[i])); } return prods; } }; @@ -380,50 +377,62 @@ class product_operator : public operator_sum { } public: + + product_operator(scalar_operator coefficient) { + operator_sum::terms.push_back({}); + operator_sum::coefficients.push_back(std::move(coefficient)); + } + // Constructor for an operator expression that represents a product // of scalar and elementary operators. // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. template...>::value, void>> - product_operator(const Args&... args) { - std::cout << "prod op constructor" << std::endl; - std::vector> ops = {}; + product_operator(scalar_operator coefficient, const Args&... args) { + operator_sum::coefficients.push_back(std::move(coefficient)); + std::vector ops = {}; ops.reserve(sizeof...(Args)); operator_sum::terms.push_back(ops); aggregate_terms(args...); } - product_operator(const std::vector>& atomic_operators) { - std::cout << "prod op constructor" << std::endl; + product_operator(scalar_operator coefficient, const std::vector& atomic_operators) { operator_sum::terms.push_back(atomic_operators); + operator_sum::coefficients.push_back(std::move(coefficient)); } - product_operator(std::vector>&& atomic_operators) { - std::cout << "prod op constructor" << std::endl; + product_operator(scalar_operator coefficient, std::vector&& atomic_operators) { operator_sum::terms.push_back(std::move(atomic_operators)); + operator_sum::coefficients.push_back(std::move(coefficient)); } // copy constructor product_operator(const product_operator &other) { operator_sum::terms = other.terms; + operator_sum::coefficients = other.coefficients; } // move constructor product_operator(product_operator &&other) { operator_sum::terms = std::move(other.terms); + operator_sum::coefficients = std::move(other.coefficients); } // assignment operator product_operator& operator=(const product_operator& other) { if (this != &other) { operator_sum::terms = other.terms; + operator_sum::coefficients = other.coefficients; } return *this; } // move assignment operator product_operator& operator=(product_operator &&other) { - operator_sum::terms = std::move(other.terms); + if (this != &other) { + this->coefficients = std::move(other.coefficients); + this->terms = std::move(other.terms); + } return *this; } @@ -437,11 +446,6 @@ class product_operator : public operator_sum { /// operator. int term_count() const { return operator_sum::terms[0].size(); } - /// FIXME: ELIMINATE THIS - std::vector> get_operators() const { - return operator_sum::terms[0]; - }; - /// @brief Return the `product_operator` as a string. std::string to_string() const; @@ -520,131 +524,14 @@ class product_operator : public operator_sum { /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. bool operator==(product_operator other); -}; - -// FIXME: check if we really need the inheritance from prod operator, and if so what it should be -// (check if this really should be its own class?) -// -> replace elementary operator with the operator handler, the current elem op is the handler for custom op -// -> scalar remains its own op class, but likely doesn't need to be convertible to operator (i.e. no inheritance) -class scalar_operator { - -private: - // If someone gave us a constant value, we will just return that - // directly to them when they call `evaluate`. - std::optional> m_constant_value; - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - -public: - scalar_operator(double value) - : m_constant_value(value), generator() {} - - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value) - : m_constant_value(value), generator() {} - - - scalar_operator(const ScalarCallbackFunction &create) - : m_constant_value(), generator(create) {} - - /// @brief Constructor that just takes a callback function with no - /// arguments. - scalar_operator(ScalarCallbackFunction &&create) - : m_constant_value() { - generator = std::move(create); - } - - // copy constructor - scalar_operator(const scalar_operator &other) - : m_constant_value(other.m_constant_value), generator(other.generator) {} - - // move constructor - scalar_operator(scalar_operator &&other) - : m_constant_value(other.m_constant_value) { - generator = std::move(other.generator); - } - - // assignment operator - scalar_operator& operator=(const scalar_operator &other) { - m_constant_value = other.m_constant_value; - generator = other.generator; - return *this; - } - - // move assignment operator - scalar_operator& operator=(scalar_operator &&other) { - m_constant_value = other.m_constant_value; - generator = std::move(other.generator); - return *this; - } - - /// NOTE: We should revisit these constructors and remove any that have - /// become unnecessary as the implementation improves. - // scalar_operator() = default; - // Copy constructor. - // scalar_operator(const scalar_operator &other); - // scalar_operator(scalar_operator &other); - - ~scalar_operator() = default; - - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {}; - - /// @brief Return the scalar operator as a concrete complex value. - std::complex - evaluate(const std::map> parameters) const; - - // Return the scalar operator as a 1x1 matrix. This is needed for - // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions, - const std::map> parameters) const; - - // Arithmetic overloads against other operator types. - scalar_operator operator*(double other) const; - scalar_operator operator/(double other) const; - scalar_operator operator+(double other) const; - scalar_operator operator-(double other) const; - scalar_operator& operator*=(double other); - scalar_operator& operator/=(double other); - scalar_operator& operator+=(double other); - scalar_operator& operator-=(double other); - scalar_operator operator*(std::complex other) const; - scalar_operator operator/(std::complex other) const; - scalar_operator operator+(std::complex other) const; - scalar_operator operator-(std::complex other) const; - scalar_operator& operator*=(std::complex other); - scalar_operator& operator/=(std::complex other); - scalar_operator& operator+=(std::complex other); - scalar_operator& operator-=(std::complex other); - scalar_operator operator*(const scalar_operator &other) const; - scalar_operator operator/(const scalar_operator &other) const; - scalar_operator operator+(const scalar_operator &other) const; - scalar_operator operator-(const scalar_operator &other) const; - scalar_operator& operator*=(const scalar_operator &other); - scalar_operator& operator/=(const scalar_operator &other); - scalar_operator& operator+=(const scalar_operator &other); - scalar_operator& operator-=(const scalar_operator &other); - /// TODO: implement and test pow - friend scalar_operator operator*(double other, const scalar_operator &self); - friend scalar_operator operator/(double other, const scalar_operator &self); - friend scalar_operator operator+(double other, const scalar_operator &self); - friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, const scalar_operator &self); - friend scalar_operator operator/(std::complex other, const scalar_operator &self); - friend scalar_operator operator+(std::complex other, const scalar_operator &self); - friend scalar_operator operator-(std::complex other, const scalar_operator &self); + /// FIXME: Protect this once I can do deeper testing in `unittests`. + // protected: + std::vector get_terms() const { + return operator_sum::terms[0]; } - // /// @brief Returns true if other is a scalar operator with the same - // /// generator. - // bool operator==(scalar_operator other); + scalar_operator get_coefficient() const { + return operator_sum::coefficients[0]; } }; @@ -669,15 +556,11 @@ class elementary_operator { /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. elementary_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), degrees(degrees) { - std::cout << "elem op constructor" << std::endl; - } + : id(operator_id), degrees(degrees) {} // constructor elementary_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), degrees(std::move(degrees)) { - std::cout << "elem op constructor" << std::endl; - } + : id(operator_id), degrees(std::move(degrees)) {} // copy constructor elementary_operator(const elementary_operator &other) @@ -748,7 +631,9 @@ class elementary_operator { /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + bool operator==(const elementary_operator &other) const { + return this->id == other.id && this->degrees == other.degrees; + } // Predefined operators. static elementary_operator identity(int degree); diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 0127e0bc39..728cb9b7a9 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -103,6 +103,15 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // return mat.exp(); // } +void assert_product_equal(const cudaq::product_operator &got, + const cudaq::product_operator &expected) { + + auto sumterms_prod = ((cudaq::operator_sum)got).get_terms(); + ASSERT_TRUE(sumterms_prod.size() == 1); + ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected.get_coefficient().evaluate({})); + ASSERT_TRUE(got.get_terms() == expected.get_terms()); +} + } // namespace utils_0 TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { @@ -236,8 +245,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + auto expected = cudaq::product_operator(const_scale_factor, self); + utils_0::assert_product_equal(product, expected); + utils_0::assert_product_equal(reverse, expected); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -263,8 +273,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + auto expected = cudaq::product_operator(other, self); + utils_0::assert_product_equal(product, expected); + utils_0::assert_product_equal(reverse, expected); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index ac8fc63887..57e8ead2ad 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -234,18 +234,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto product = sum * cudaq::scalar_operator(1.0); - auto reverse = cudaq::scalar_operator(1.0) * sum; + auto product = sum * cudaq::scalar_operator(0.1); + auto reverse = cudaq::scalar_operator(0.1) * sum; ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); } } @@ -278,11 +280,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - sum *= cudaq::scalar_operator(1.0); + sum *= cudaq::scalar_operator(0.1); ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); } } @@ -322,11 +325,13 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(reverse.term_count() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); } } @@ -363,7 +368,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + std::cout << "GOT: " << term.get_coefficient().evaluate({}) << std::endl; + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); } } @@ -400,11 +407,13 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(reverse.term_count() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); } } @@ -443,7 +452,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index cf4660af80..64367c6779 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -350,11 +350,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); + ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + auto product = value_0 * product_op; auto reverse = product_op * value_0; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); + ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -366,11 +371,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); + ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + auto product = 2.0 * product_op; auto reverse = product_op * 2.0; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -381,13 +391,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); + ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + + auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); + ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == scalar_op.evaluate({})); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -400,7 +415,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= value_0; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -412,7 +428,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= 2.0; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -422,11 +439,12 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { { auto product = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); - + auto scalar_op = cudaq::scalar_operator(0.1); product *= scalar_op; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); + ASSERT_TRUE(scalar_op.evaluate({}) == std::complex(0.1)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); From ac469ffb29a8c750eecd3ce61a8abe5171f0ab5c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 27 Jan 2025 21:40:43 +0000 Subject: [PATCH 061/311] getting rid of all elementary operator overloads Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 188 ++++-------------- runtime/cudaq/dynamics/product_operators.cpp | 12 ++ runtime/cudaq/operators.h | 54 ++--- .../dynamics/elementary_ops_arithmetic.cpp | 17 +- unittests/dynamics/elementary_ops_simple.cpp | 1 - 5 files changed, 73 insertions(+), 199 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index b9b32e4682..8e5f42b7e3 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -17,7 +17,7 @@ namespace cudaq { std::map elementary_operator::m_ops = {}; -elementary_operator elementary_operator::identity(int degree) { +product_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. @@ -36,41 +36,37 @@ elementary_operator elementary_operator::identity(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::zero(int degree) { +product_operator elementary_operator::zero(int degree) { std::string op_id = "zero"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when // the user invokes this later on via, e.g, `to_matrix()`. - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); return mat; }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::annihilate(int degree) { +product_operator elementary_operator::annihilate(int degree) { std::string op_id = "annihilate"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -80,19 +76,17 @@ elementary_operator elementary_operator::annihilate(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::create(int degree) { +product_operator elementary_operator::create(int degree) { std::string op_id = "create"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -102,19 +96,17 @@ elementary_operator elementary_operator::create(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::position(int degree) { +product_operator elementary_operator::position(int degree) { std::string op_id = "position"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) @@ -128,19 +120,17 @@ elementary_operator elementary_operator::position(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::momentum(int degree) { +product_operator elementary_operator::momentum(int degree) { std::string op_id = "momentum"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) @@ -154,19 +144,17 @@ elementary_operator elementary_operator::momentum(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::number(int degree) { +product_operator elementary_operator::number(int degree) { std::string op_id = "number"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -176,19 +164,17 @@ elementary_operator elementary_operator::number(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::parity(int degree) { +product_operator elementary_operator::parity(int degree) { std::string op_id = "parity"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -198,20 +184,18 @@ elementary_operator elementary_operator::parity(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator +product_operator elementary_operator::displace(int degree, std::complex amplitude) { std::string op_id = "displace"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; // if (op.m_ops.find(op_id) == op.m_ops.end()) { - // auto func = [&](std::map dimensions, + // auto func = [&, degree](std::map dimensions, // std::map> _none) { - // auto degree = op.degrees[0]; // std::size_t dimension = dimensions[degree]; // auto temp_mat = matrix_2(dimension, dimension); // // // displace = exp[ (amplitude * create) - (conj(amplitude) * @@ -233,10 +217,10 @@ elementary_operator::displace(int degree, std::complex amplitude) { // op.define(op_id, op.expected_dimensions, func); // } throw std::runtime_error("currently have a bug in implementation."); - return op; + return product_operator(1., op); } -elementary_operator +product_operator elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } @@ -249,106 +233,4 @@ matrix_2 elementary_operator::to_matrix( return m_ops[id].generator(dimensions, parameters); } -// left-hand arithmetics - -product_operator operator*(double other, const elementary_operator &self) { - return product_operator(other, self); -} - -operator_sum operator+(double other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., self)); -} - -operator_sum operator-(double other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, -1. * self); -} - -product_operator operator*(std::complex other, const elementary_operator &self) { - return product_operator(other, self); -} - -operator_sum operator+(std::complex other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., self)); -} - -operator_sum operator-(std::complex other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, -1. * self); -} - -product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator(other, self); -} - -operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., self)); -} - -operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, -1. * self); -} - -// right-hand arithmetics - -product_operator elementary_operator::operator*(double other) const { - return product_operator(other, *this); -} - -operator_sum elementary_operator::operator+(double other) const { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -operator_sum elementary_operator::operator-(double other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -product_operator elementary_operator::operator*(std::complex other) const { - return product_operator(other, *this); -} - -operator_sum elementary_operator::operator+(std::complex other) const { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -operator_sum elementary_operator::operator-(std::complex other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -product_operator elementary_operator::operator*(const scalar_operator &other) const { - return product_operator(other, *this); -} - -operator_sum elementary_operator::operator+(const scalar_operator &other) const { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -operator_sum elementary_operator::operator-(const scalar_operator &other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -product_operator elementary_operator::operator*(const elementary_operator &other) const { - return product_operator(1., *this, other); -} - -operator_sum elementary_operator::operator+(const elementary_operator &other) const { - auto term1 = product_operator(1., *this); - auto term2 = product_operator(1., other); - return operator_sum(term1, term2); -} - -operator_sum elementary_operator::operator-(const elementary_operator &other) const { - return operator_sum(product_operator(1., *this), -1. * other); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index d8bb0dc5f1..356fe4b25b 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -142,6 +142,18 @@ matrix_2 product_operator::to_matrix( // return out; // } + +// FIXME: remove - to be replaced with the general implementation for product op +template<> +matrix_2 product_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + if (this->get_coefficient() != scalar_operator(1.) || this->term_count() != 1) + throw std::runtime_error("not implemented"); + return this->get_terms()[0].to_matrix(dimensions, parameters); +} + + // Degrees property template std::vector product_operator::degrees() const { diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 7702242b6c..a2cc48a570 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -143,7 +143,13 @@ class scalar_operator { // /// @brief Returns true if other is a scalar operator with the same // /// generator. - // bool operator==(scalar_operator other); + bool operator==(scalar_operator other) { + if (this->m_constant_value.has_value() && other.m_constant_value.has_value()) { + return this->m_constant_value == other.m_constant_value; + } else { + throw std::runtime_error("not implemented"); + } + } }; @@ -605,30 +611,6 @@ class elementary_operator { to_matrix(const std::map dimensions, const std::map> parameters) const; - // Arithmetic overloads - product_operator operator*(double other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - product_operator operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - product_operator operator*(const elementary_operator &other) const; - operator_sum operator+(const elementary_operator &other) const; - operator_sum operator-(const elementary_operator &other) const; - - friend product_operator operator*(double other, const elementary_operator &self); - friend operator_sum operator+(double other, const elementary_operator &self); - friend operator_sum operator-(double other, const elementary_operator &self); - friend product_operator operator*(std::complex other, const elementary_operator &self); - friend operator_sum operator+(std::complex other, const elementary_operator &self); - friend operator_sum operator-(std::complex other, const elementary_operator &self); - friend product_operator operator*(const scalar_operator &other, const elementary_operator &self); - friend operator_sum operator+(const scalar_operator &other, const elementary_operator &self); - friend operator_sum operator-(const scalar_operator &other, const elementary_operator &self); - /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. bool operator==(const elementary_operator &other) const { @@ -636,18 +618,18 @@ class elementary_operator { } // Predefined operators. - static elementary_operator identity(int degree); - static elementary_operator zero(int degree); - static elementary_operator annihilate(int degree); - static elementary_operator create(int degree); - static elementary_operator momentum(int degree); - static elementary_operator number(int degree); - static elementary_operator parity(int degree); - static elementary_operator position(int degree); - /// FIXME: - static elementary_operator squeeze(int degree, + static product_operator identity(int degree); + static product_operator zero(int degree); + static product_operator annihilate(int degree); + static product_operator create(int degree); + static product_operator momentum(int degree); + static product_operator number(int degree); + static product_operator parity(int degree); + static product_operator position(int degree); + /// FIXME: amplitude should be a parameter that is only defined upon evaluation + static product_operator squeeze(int degree, std::complex amplitude); - static elementary_operator displace(int degree, + static product_operator displace(int degree, std::complex amplitude); /// @brief Adds the definition of an elementary operator with the given id to diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 728cb9b7a9..a88d3c7b38 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -104,12 +104,13 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } void assert_product_equal(const cudaq::product_operator &got, - const cudaq::product_operator &expected) { + const std::complex &expected_coefficient, + const std::vector &expected_terms) { auto sumterms_prod = ((cudaq::operator_sum)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); - ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected.get_coefficient().evaluate({})); - ASSERT_TRUE(got.get_terms() == expected.get_terms()); + ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected_coefficient); + ASSERT_TRUE(got.get_terms() == expected_terms); } } // namespace utils_0 @@ -245,9 +246,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - auto expected = cudaq::product_operator(const_scale_factor, self); - utils_0::assert_product_equal(product, expected); - utils_0::assert_product_equal(reverse, expected); + utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -273,9 +273,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - auto expected = cudaq::product_operator(other, self); - utils_0::assert_product_equal(product, expected); - utils_0::assert_product_equal(reverse, expected); + utils_0::assert_product_equal(product, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp index b45d3d8952..3b12805d3a 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -108,7 +108,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Identity operator. { for (auto level_count : levels) { - // cudaq::operators::identity(int degree) auto id = cudaq::elementary_operator::identity(degree_index); auto got_id = id.to_matrix({{degree_index, level_count}}, {}); auto want_id = utils::id_matrix(level_count); From 26e356c41321f640db589b250f2a0c0641e47ed5 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 28 Jan 2025 13:37:02 +0000 Subject: [PATCH 062/311] clean up for scalars Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/scalar_operators.cpp | 255 ++++++++++-------- runtime/cudaq/operators.h | 92 +++---- .../dynamics/elementary_ops_arithmetic.cpp | 6 +- unittests/dynamics/operator_sum.cpp | 20 +- .../dynamics/product_operators_arithmetic.cpp | 26 +- unittests/dynamics/scalar_ops_arithmetic.cpp | 58 ++-- unittests/dynamics/scalar_ops_simple.cpp | 16 +- 7 files changed, 243 insertions(+), 230 deletions(-) diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 2463910cbd..f7f43536e7 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,11 +14,63 @@ namespace cudaq { +// constructors and destructors + +scalar_operator::scalar_operator(double value) + : constant_value(value), generator() {} + +scalar_operator::scalar_operator(std::complex value) + : constant_value(value), generator() {} + +scalar_operator::scalar_operator(const ScalarCallbackFunction &create) + : constant_value(), generator(create) {} + +scalar_operator::scalar_operator(ScalarCallbackFunction &&create) + : constant_value() { + generator = std::move(create); +} + +scalar_operator::scalar_operator(const scalar_operator &other) + : constant_value(other.constant_value), generator(other.generator) {} + +scalar_operator::scalar_operator(scalar_operator &&other) + : constant_value(other.constant_value) { + generator = std::move(other.generator); +} + +// assignments + +scalar_operator& scalar_operator::operator=(const scalar_operator &other) { + if (this != &other) { + constant_value = other.constant_value; + generator = other.generator; + } + return *this; +} + +scalar_operator& scalar_operator::operator=(scalar_operator &&other) { + if (this != &other) { + constant_value = other.constant_value; + generator = std::move(other.generator); + } + return *this; +} + +// comparison + +bool scalar_operator::operator==(scalar_operator other) { + if (this->constant_value.has_value() && other.constant_value.has_value()) { + return this->constant_value == other.constant_value; + } else { + throw std::runtime_error("not implemented"); + } +} + // evaluations std::complex scalar_operator::evaluate( const std::map> parameters) const { - if (m_constant_value.has_value()) return m_constant_value.value(); + if (constant_value.has_value()) return constant_value.value(); else return generator(parameters); } @@ -30,118 +82,41 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } -// left-hand arithmetics - -#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(double other, const scalar_operator &self) { \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ - } - -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(std::complex other, \ - const scalar_operator &self) { \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); - // right-hand arithmetics -#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ - scalar_operator scalar_operator::operator op(double other) const { \ - auto newGenerator = \ - [=, *this](std::map> parameters) { \ - return this->evaluate(parameters) op other; \ - }; \ - return scalar_operator(newGenerator); \ - } - -ARITHMETIC_OPERATIONS_DOUBLES(*); -ARITHMETIC_OPERATIONS_DOUBLES(/); -ARITHMETIC_OPERATIONS_DOUBLES(+); -ARITHMETIC_OPERATIONS_DOUBLES(-); - -#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op(double other) { \ - if (this->m_constant_value.has_value()) { \ - this->m_constant_value.value() op other; \ - return *this; \ +#define ARITHMETIC_OPERATIONS(op, otherTy) \ + scalar_operator scalar_operator::operator op(otherTy other) const { \ + if (this->constant_value.has_value()) { \ + return scalar_operator(this->constant_value.value() op other); \ } \ - /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in-place. */ \ - scalar_operator prevSelf(*this); \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return prevSelf.evaluate(parameters) op other; \ - }; \ - this->generator = newGenerator; \ - return *this; \ - } - -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator scalar_operator::operator op( \ - std::complex other) const{ \ auto newGenerator = \ - [=, *this](std::map> parameters) { \ - return this->evaluate(parameters) op other; \ - }; \ + [other, generator = this->generator]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ return scalar_operator(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op( \ - std::complex other) { \ - if (this->m_constant_value.has_value()) { \ - this->m_constant_value.value() op other; \ - return *this; \ - } \ - /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in-place. */ \ - scalar_operator prevSelf(*this); \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return prevSelf.evaluate(parameters) op other; \ - }; \ - this->generator = newGenerator; \ - return *this; \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS(*, double); +ARITHMETIC_OPERATIONS(/, double); +ARITHMETIC_OPERATIONS(+, double); +ARITHMETIC_OPERATIONS(-, double); +ARITHMETIC_OPERATIONS(*, std::complex); +ARITHMETIC_OPERATIONS(/, std::complex); +ARITHMETIC_OPERATIONS(+, std::complex); +ARITHMETIC_OPERATIONS(-, std::complex); #define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ scalar_operator scalar_operator::operator op( \ const scalar_operator &other) const { \ + if (this->constant_value.has_value() && \ + other.constant_value.has_value()) { \ + auto res = this->constant_value.value() op other.constant_value.value(); \ + return scalar_operator(res); \ + } \ auto newGenerator = \ - [=, *this](std::map> parameters) { \ + [other, *this]( \ + std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(newGenerator); \ @@ -152,20 +127,42 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(/); ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); +#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ + scalar_operator& scalar_operator::operator op(otherTy other) { \ + if (this->constant_value.has_value()) { \ + this->constant_value.value() op other; \ + return *this; \ + } \ + auto newGenerator = \ + [other, generator = std::move(this->generator)]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ + this->generator = newGenerator; \ + return *this; \ + } + +ARITHMETIC_OPERATIONS_ASSIGNMENT(*=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(/=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(*=, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(/=, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, std::complex); + #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op( \ const scalar_operator &other) { \ - if (this->m_constant_value.has_value() && \ - other.m_constant_value.has_value()) { \ - this->m_constant_value.value() op other.m_constant_value.value(); \ - return *this; \ + if (this->constant_value.has_value() && \ + other.constant_value.has_value()) { \ + this->constant_value.value() op other.constant_value.value(); \ + return *this; \ } \ - /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in-place. */ \ - scalar_operator prevSelf(*this); \ auto newGenerator = \ - [=](std::map> parameters) { \ - return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ + [other, *this]( \ + std::map> parameters) { \ + return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ this->generator = newGenerator; \ return *this; \ @@ -176,4 +173,42 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ + scalar_operator operator op(scalar_operator &&self, otherTy other) { \ + return std::move(self op##= other); \ + } + +ARITHMETIC_OPERATIONS_RVALUE(*, double); +ARITHMETIC_OPERATIONS_RVALUE(/, double); +ARITHMETIC_OPERATIONS_RVALUE(+, double); +ARITHMETIC_OPERATIONS_RVALUE(-, double); +ARITHMETIC_OPERATIONS_RVALUE(*, std::complex); +ARITHMETIC_OPERATIONS_RVALUE(/, std::complex); +ARITHMETIC_OPERATIONS_RVALUE(+, std::complex); +ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); + +// left-hand arithmetics + +#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ + scalar_operator operator op(otherTy other, const scalar_operator &self) { \ + if (self.constant_value.has_value()) { \ + return scalar_operator(other op self.constant_value.value()); \ + } \ + auto newGenerator = \ + [other, generator = self.generator]( \ + std::map> parameters) { \ + return other op generator(parameters); \ + }; \ + return scalar_operator(newGenerator); \ + } + +ARITHMETIC_OPERATIONS_REVERSE(*, double); +ARITHMETIC_OPERATIONS_REVERSE(/, double); +ARITHMETIC_OPERATIONS_REVERSE(+, double); +ARITHMETIC_OPERATIONS_REVERSE(-, double); +ARITHMETIC_OPERATIONS_REVERSE(*, std::complex); +ARITHMETIC_OPERATIONS_REVERSE(/, std::complex); +ARITHMETIC_OPERATIONS_REVERSE(+, std::complex); +ARITHMETIC_OPERATIONS_REVERSE(-, std::complex); + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index a2cc48a570..df9dac7452 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -26,7 +26,7 @@ class scalar_operator { private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. - std::optional> m_constant_value; + std::optional> constant_value; /// @brief The function that generates the value of the scalar operator. /// The function can take a vector of complex-valued arguments @@ -34,78 +34,55 @@ class scalar_operator { ScalarCallbackFunction generator; public: - scalar_operator(double value) - : m_constant_value(value), generator() {} + + // constructors and destructors + + scalar_operator(double value); /// @brief Constructor that just takes and returns a complex double value. /// @NOTE: This replicates the behavior of the python `scalar_operator::const` /// without the need for an extra member function. - scalar_operator(std::complex value) - : m_constant_value(value), generator() {} - + scalar_operator(std::complex value); - scalar_operator(const ScalarCallbackFunction &create) - : m_constant_value(), generator(create) {} + scalar_operator(const ScalarCallbackFunction &create); /// @brief Constructor that just takes a callback function with no /// arguments. - scalar_operator(ScalarCallbackFunction &&create) - : m_constant_value() { - generator = std::move(create); - } + scalar_operator(ScalarCallbackFunction &&create); // copy constructor - scalar_operator(const scalar_operator &other) - : m_constant_value(other.m_constant_value), generator(other.generator) {} + scalar_operator(const scalar_operator &other); // move constructor - scalar_operator(scalar_operator &&other) - : m_constant_value(other.m_constant_value) { - generator = std::move(other.generator); - } + scalar_operator(scalar_operator &&other); + + ~scalar_operator() = default; + + // assignments // assignment operator - scalar_operator& operator=(const scalar_operator &other) { - if (this != &other) { - m_constant_value = other.m_constant_value; - generator = other.generator; - } - return *this; - } + scalar_operator& operator=(const scalar_operator &other); // move assignment operator - scalar_operator& operator=(scalar_operator &&other) { - if (this != &other) { - m_constant_value = other.m_constant_value; - generator = std::move(other.generator); - } - return *this; - } + scalar_operator& operator=(scalar_operator &&other); - /// NOTE: We should revisit these constructors and remove any that have - /// become unnecessary as the implementation improves. - // scalar_operator() = default; - // Copy constructor. - // scalar_operator(const scalar_operator &other); - // scalar_operator(scalar_operator &other); + // comparison - ~scalar_operator() = default; + bool operator==(scalar_operator other); - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {}; + // evaluations /// @brief Return the scalar operator as a concrete complex value. std::complex - evaluate(const std::map> parameters) const; + evaluate(const std::map> parameters = {}) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions, - const std::map> parameters) const; + matrix_2 to_matrix(const std::map dimensions = {}, + const std::map> parameters = {}) const; + + // right-hand arithmetics - // Arithmetic overloads against other operator types. scalar_operator operator*(double other) const; scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; @@ -132,6 +109,17 @@ class scalar_operator { scalar_operator& operator-=(const scalar_operator &other); /// TODO: implement and test pow + friend scalar_operator operator*(scalar_operator &&self, double other); + friend scalar_operator operator/(scalar_operator &&self, double other); + friend scalar_operator operator+(scalar_operator &&self, double other); + friend scalar_operator operator-(scalar_operator &&self, double other); + friend scalar_operator operator+(scalar_operator &&self, std::complex other); + friend scalar_operator operator/(scalar_operator &&self, std::complex other); + friend scalar_operator operator+(scalar_operator &&self, std::complex other); + friend scalar_operator operator-(scalar_operator &&self, std::complex other); + + // left-hand arithmetics + friend scalar_operator operator*(double other, const scalar_operator &self); friend scalar_operator operator/(double other, const scalar_operator &self); friend scalar_operator operator+(double other, const scalar_operator &self); @@ -140,16 +128,6 @@ class scalar_operator { friend scalar_operator operator/(std::complex other, const scalar_operator &self); friend scalar_operator operator+(std::complex other, const scalar_operator &self); friend scalar_operator operator-(std::complex other, const scalar_operator &self); - - // /// @brief Returns true if other is a scalar operator with the same - // /// generator. - bool operator==(scalar_operator other) { - if (this->m_constant_value.has_value() && other.m_constant_value.has_value()) { - return this->m_constant_value == other.m_constant_value; - } else { - throw std::runtime_error("not implemented"); - } - } }; diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index a88d3c7b38..53556ce83a 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -109,7 +109,7 @@ void assert_product_equal(const cudaq::product_operator)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); - ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected_coefficient); + ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); } @@ -273,8 +273,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 57e8ead2ad..4ae7c55b74 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -242,12 +242,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -285,7 +285,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -326,12 +326,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } } @@ -369,8 +369,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - std::cout << "GOT: " << term.get_coefficient().evaluate({}) << std::endl; - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); + std::cout << "GOT: " << term.get_coefficient().evaluate() << std::endl; + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } } @@ -408,12 +408,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } @@ -453,7 +453,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 64367c6779..2a585e86a6 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -351,15 +351,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); ASSERT_TRUE(product_op.term_count() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); - ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == value_0); + ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -372,15 +372,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); ASSERT_TRUE(product_op.term_count() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); - ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -393,7 +393,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); ASSERT_TRUE(product_op.term_count() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; @@ -401,8 +401,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); - ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == scalar_op.evaluate({})); + ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -416,7 +416,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= value_0; ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); + ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -429,7 +429,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= 2.0; ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -443,8 +443,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= scalar_op; ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); - ASSERT_TRUE(scalar_op.evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); + ASSERT_TRUE(scalar_op.evaluate() == std::complex(0.1)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index b6d237eba4..c3ef485228 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -31,10 +31,10 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_1 + scalar_op; // function + scalar_op; auto reverse_order_op = scalar_op + value_1; - EXPECT_NEAR(std::abs(scalar_op.evaluate({})), std::abs(value_0), 1e-5); + EXPECT_NEAR(std::abs(scalar_op.evaluate()), std::abs(value_0), 1e-5); - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_1 + value_0; EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); @@ -42,7 +42,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { // Checking composition of many scalar operators. auto third_op = new_scalar_op + reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value + want_value), 1e-5); } @@ -74,15 +74,15 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_3 - scalar_op; auto reverse_order_op = scalar_op - value_3; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op - reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); auto want_value = (value_3 - value_1) - (value_1 - value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -114,15 +114,15 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_3 * scalar_op; auto reverse_order_op = scalar_op * value_3; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op * reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); auto want_value = (value_3 * value_2) * (value_2 * value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -154,15 +154,15 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_3 / scalar_op; auto reverse_order_op = scalar_op / value_3; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 / value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); auto want_value = (value_3 / value_2) / (value_2 / value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -192,7 +192,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_0); scalar_op += value_0; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); } @@ -210,7 +210,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_0); scalar_op -= value_0; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); } @@ -228,7 +228,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op *= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); } @@ -246,7 +246,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op /= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); } @@ -293,8 +293,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op + scalar_op; auto reverse_order_op = scalar_op + other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_1 + value_0; EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); @@ -327,8 +327,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op - scalar_op; auto reverse_order_op = scalar_op - other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_1 - value_2; EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); @@ -361,8 +361,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op * scalar_op; auto reverse_order_op = scalar_op * other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_3 * value_2; auto reverse_want_value = value_2 * value_3; @@ -396,8 +396,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op / scalar_op; auto reverse_order_op = scalar_op / other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_2 / value_0; auto reverse_want_value = value_0 / value_2; @@ -429,7 +429,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto other = cudaq::scalar_operator(value_0); scalar_op += other; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); } @@ -455,7 +455,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto scalar_op = cudaq::scalar_operator(value_0); scalar_op -= value_0; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); } @@ -473,7 +473,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op *= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); } @@ -491,7 +491,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op /= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); } diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index 158ab49841..5ec80350f4 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -24,10 +24,10 @@ TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { auto operator_2 = cudaq::scalar_operator(value_2); auto operator_3 = cudaq::scalar_operator(value_3); - auto got_value_0 = operator_0.evaluate({}); - auto got_value_1 = operator_1.evaluate({}); - auto got_value_2 = operator_2.evaluate({}); - auto got_value_3 = operator_3.evaluate({}); + auto got_value_0 = operator_0.evaluate(); + auto got_value_1 = operator_1.evaluate(); + auto got_value_2 = operator_2.evaluate(); + auto got_value_3 = operator_3.evaluate(); EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); @@ -78,10 +78,10 @@ TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { auto operator_2 = cudaq::scalar_operator(value_2); auto operator_3 = cudaq::scalar_operator(value_3); - auto got_value_0 = operator_0.evaluate({}); - auto got_value_1 = operator_1.evaluate({}); - auto got_value_2 = operator_2.evaluate({}); - auto got_value_3 = operator_3.evaluate({}); + auto got_value_0 = operator_0.evaluate(); + auto got_value_1 = operator_1.evaluate(); + auto got_value_2 = operator_2.evaluate(); + auto got_value_3 = operator_3.evaluate(); EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); From 357a136d230e0ee7de0c847a6ca9fab265129a31 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 28 Jan 2025 16:33:05 +0000 Subject: [PATCH 063/311] some clean up of op sum Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 321 +++++++++++------- runtime/cudaq/dynamics/product_operators.cpp | 2 +- runtime/cudaq/dynamics/scalar_operators.cpp | 20 +- runtime/cudaq/operators.h | 169 +++------ .../dynamics/elementary_ops_arithmetic.cpp | 52 +-- unittests/dynamics/operator_sum.cpp | 92 ++--- .../dynamics/product_operators_arithmetic.cpp | 86 ++--- 7 files changed, 376 insertions(+), 366 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index f2c7b75311..4ea1d3dcde 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -11,130 +11,215 @@ #include #include +#include +#include namespace cudaq { -// std::vector> -// operator_sum::canonicalize_product(product_operator &prod) const { -// std::vector> -// canonicalized_terms; - -// std::vector all_degrees; -// std::vector scalars; -// std::vector non_scalars; - -// for (const auto &op : prod.get_operators()) { -// if (std::holds_alternative(op)) { -// scalars.push_back(*std::get(op)); -// } else { -// non_scalars.push_back(*std::get(op)); -// all_degrees.insert(all_degrees.end(), -// std::get(op).degrees.begin(), -// std::get(op).degrees.end()); -// } -// } - -// if (all_degrees.size() == -// std::set(all_degrees.begin(), all_degrees.end()).size()) { -// std::sort(non_scalars.begin(), non_scalars.end(), -// [](const HandlerTy &a, const HandlerTy &b) { -// return a.degrees < b.degrees; -// }); -// } - -// for (size_t i = 0; std::min(scalars.size(), non_scalars.size()); i++) { -// canonicalized_terms.push_back(std::make_tuple(scalars[i], non_scalars[i])); -// } - -// return canonicalized_terms; -// } - -// std::vector> -// operator_sum::_canonical_terms() const { -// std::vector> terms; -// // for (const auto &term : terms) { -// // auto canonicalized = canonicalize_product(term); -// // terms.insert(terms.end(), canonicalized.begin(), canonicalized.end()); -// // } - -// // std::sort(terms.begin(), terms.end(), [](const auto &a, const auto &b) { -// // // return std::to_string(product_operator(a)) < -// // // std::to_string(product_operator(b)); -// // return product_operator(a).to_string() < -// product_operator(b).to_string(); -// // }); - -// return terms; -// } - -// operator_sum operator_sum::canonicalize() const { -// std::vector canonical_terms; -// for (const auto &term : _canonical_terms()) { -// canonical_terms.push_back(product_operator(term)); -// } -// return operator_sum(canonical_terms); -// } - -// bool operator_sum::operator==(const operator_sum &other) const { -// return _canonical_terms() == other._canonical_terms(); -// } - -// // Degrees property -// std::vector operator_sum::degrees() const { -// std::set unique_degrees; -// for (const auto &term : terms) { -// for (const auto &op : term.get_operators()) { -// unique_degrees.insert(op.get_degrees().begin(), -// op.get_degrees().end()); -// } -// } - -// return std::vector(unique_degrees.begin(), unique_degrees.end()); -// } - -// // Parameters property -// std::map operator_sum::parameters() const { -// std::map param_map; -// for (const auto &term : terms) { -// for (const auto &op : term.get_operators()) { -// auto op_params = op.parameters(); -// param_map.insert(op_params.begin(), op.params.end()); -// } -// } - -// return param_map; -// } - -// // Check if all terms are spin operators -// bool operator_sum::_is_spinop() const { -// return std::all_of( -// terms.begin(), terms.end(), [](product_operator &term) { -// return std::all_of(term.get_operators().begin(), -// term.get_operators().end(), -// [](const Operator &op) { return op.is_spinop(); -// }); -// }); -// } +// private methods + +template +requires std::derived_from +std::vector> operator_sum::canonicalize_product(product_operator &prod) const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +std::vector> operator_sum::_canonical_terms() const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +void operator_sum::aggregate_terms(const product_operator &head) { + this->terms.push_back(head.terms[0]); + this->coefficients.push_back(head.coefficients[0]); +} + +template +requires std::derived_from +template +void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { + this->terms.push_back(head.terms[0]); + this->coefficients.push_back(head.coefficients[0]); + aggregate_terms(std::forward(args)...); +} + +template +std::vector> operator_sum::canonicalize_product(product_operator &prod) const; + +template +std::vector> operator_sum::_canonical_terms() const; + +template +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2); + +template +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); + +// read-only properties + +template +requires std::derived_from +std::vector operator_sum::degrees() const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +int operator_sum::n_terms() const { + return this->terms.size(); +} + +template +requires std::derived_from +std::vector> operator_sum::get_terms() const { + std::vector> prods; + prods.reserve(this->terms.size()); + for (size_t i = 0; i < this->terms.size(); ++i) { + prods.push_back(product_operator(this->coefficients[i], this->terms[i])); + } + return prods; +} + +template +std::vector operator_sum::degrees() const; + +template +int operator_sum::n_terms() const; + +template +std::vector> operator_sum::get_terms() const; + +// constructors + +template +requires std::derived_from +template +operator_sum::operator_sum(const Args&... args) { + this->terms.reserve(sizeof...(Args)); + this->coefficients.reserve(sizeof...(Args)); + aggregate_terms(args...); +} + +template +requires std::derived_from +operator_sum::operator_sum(const std::vector> &terms) { + this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(term.terms[0]); + this->coefficients.push_back(term.coefficients[0]); + } +} + +template +requires std::derived_from +operator_sum::operator_sum(std::vector> &&terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(std::move(term.terms[0])); + this->coefficients.push_back(std::move(term.coefficients[0])); + } +} + +template +requires std::derived_from +operator_sum::operator_sum(const operator_sum &other) + : coefficients(other.coefficients), terms(other.terms) {} + +template +requires std::derived_from +operator_sum::operator_sum(operator_sum &&other) + : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} + +template +operator_sum::operator_sum(const product_operator &item1); + +template +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2); + +template +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); + +template +operator_sum::operator_sum(const std::vector> &terms); + +template +operator_sum::operator_sum(std::vector> &&terms); + +template +operator_sum::operator_sum(const operator_sum &other); + +template +operator_sum::operator_sum(operator_sum &&other); + +// assignments + +template +requires std::derived_from +operator_sum& operator_sum::operator=(const operator_sum &other) { + if (this != &other) { + coefficients = other.coefficients; + terms = other.terms; + } + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator=(operator_sum &&other) { + if (this != &other) { + coefficients = std::move(other.coefficients); + terms = std::move(other.terms); + } + return *this; +} + +template +operator_sum& operator_sum::operator=(const operator_sum& other); + +template +operator_sum& operator_sum::operator=(operator_sum &&other); // evaluations -/// FIXME: -// tensor -// operator_sum::to_matrix(const std::map &dimensions, -// const std::map ¶ms) const { -// // todo -// } - -// std::string operator_sum::to_string() const { -// std::string result; -// // for (const auto &term : terms) { -// // result += term.to_string() + " + "; -// // } -// // // Remove last " + " -// // if (!result.empty()) -// // result.pop_back(); -// return result; -// } +template +requires std::derived_from +std::string operator_sum::to_string() const { + throw std::runtime_error("not implemented"); +} +template +requires std::derived_from +matrix_2 operator_sum::to_matrix(const std::map &dimensions, + const std::map ¶ms) const { + throw std::runtime_error("not implemented"); +} + +template +std::string operator_sum::to_string() const; + +template +matrix_2 operator_sum::to_matrix(const std::map &dimensions, + const std::map ¶ms) const; + +// comparisons + +template +requires std::derived_from +bool operator_sum::operator==(const operator_sum &other) const { + throw std::runtime_error("not implemented"); +} + +template +bool operator_sum::operator==(const operator_sum &other) const; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 356fe4b25b..53fc7acaf4 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -148,7 +148,7 @@ template<> matrix_2 product_operator::to_matrix( std::map dimensions, std::map> parameters) { - if (this->get_coefficient() != scalar_operator(1.) || this->term_count() != 1) + if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) throw std::runtime_error("not implemented"); return this->get_terms()[0].to_matrix(dimensions, parameters); } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index f7f43536e7..f453fbf62f 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -56,16 +56,6 @@ scalar_operator& scalar_operator::operator=(scalar_operator &&other) { return *this; } -// comparison - -bool scalar_operator::operator==(scalar_operator other) { - if (this->constant_value.has_value() && other.constant_value.has_value()) { - return this->constant_value == other.constant_value; - } else { - throw std::runtime_error("not implemented"); - } -} - // evaluations std::complex scalar_operator::evaluate( @@ -82,6 +72,16 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +// comparison + +bool scalar_operator::operator==(scalar_operator other) { + if (this->constant_value.has_value() && other.constant_value.has_value()) { + return this->constant_value == other.constant_value; + } else { + throw std::runtime_error("not implemented"); + } +} + // right-hand arithmetics #define ARITHMETIC_OPERATIONS(op, otherTy) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index df9dac7452..68b78c0a9c 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -66,10 +66,6 @@ class scalar_operator { // move assignment operator scalar_operator& operator=(scalar_operator &&other); - // comparison - - bool operator==(scalar_operator other); - // evaluations /// @brief Return the scalar operator as a concrete complex value. @@ -81,6 +77,10 @@ class scalar_operator { matrix_2 to_matrix(const std::map dimensions = {}, const std::map> parameters = {}) const; + // comparisons + + bool operator==(scalar_operator other); + // right-hand arithmetics scalar_operator operator*(double other) const; @@ -140,23 +140,17 @@ requires std::derived_from class operator_sum { private: + std::vector> canonicalize_product(product_operator &prod) const; std::vector> _canonical_terms() const; - void aggregate_terms(const product_operator& head) { - terms.push_back(head.terms[0]); - coefficients.push_back(head.coefficients[0]); - } + void aggregate_terms(const product_operator& head); template - void aggregate_terms(const product_operator &head, Args&& ... args) { - terms.push_back(head.terms[0]); - coefficients.push_back(head.coefficients[0]); - aggregate_terms(std::forward(args)...); - } + void aggregate_terms(const product_operator &head, Args&& ... args); protected: @@ -166,73 +160,43 @@ class operator_sum { public: - /// @brief Construct a `cudaq::operator_sum` given a sequence of - /// `cudaq::product_operator`'s. - /// This operator expression represents a sum of terms, where each term - /// is a product of elementary and scalar operators. + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + /// @brief Return the number of operator terms that make up this operator sum. + int n_terms() const; + + std::vector> get_terms() const; + + // constructors and destructors + template, Args>...>::value, void>> - operator_sum(const Args&... args) { - terms.reserve(sizeof...(Args)); - coefficients.reserve(sizeof...(Args)); - aggregate_terms(args...); - } + operator_sum(const Args&... args); - operator_sum(const std::vector>& terms) { - this->terms.reserve(terms.size()); - this->coefficients.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(term.terms[0]); - this->coefficients.push_back(term.coefficients[0]); - } - } + operator_sum(const std::vector> &terms); - operator_sum(std::vector>&& terms) { - this->terms.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(std::move(term.terms[0])); - this->coefficients.push_back(std::move(term.coefficients[0])); - } - } + operator_sum(std::vector> &&terms); // copy constructor - operator_sum(const operator_sum &other) - : coefficients(other.coefficients), terms(other.terms) {} + operator_sum(const operator_sum &other); // move constructor - operator_sum(operator_sum &&other) - : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} - - // assignment operator - operator_sum& operator=(const operator_sum& other) { - if (this != &other) { - coefficients = other.coefficients; - terms = other.terms; - } - return *this; - } - - // move assignment operator - operator_sum& operator=(operator_sum &&other) { - if (this != &other) { - coefficients = std::move(other.coefficients); - terms = std::move(other.terms); - } - return *this; - } + operator_sum(operator_sum &&other); ~operator_sum() = default; - operator_sum canonicalize() const; + // assignments - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees() const; + // assignment operator + operator_sum& operator=(const operator_sum &other); - bool _is_spinop() const; + // move assignment operator + operator_sum& operator=(operator_sum &&other); - /// TODO: implement - // template - // TEval _evaluate(OperatorArithmetics &arithmetics) const; + // evaluations /// @brief Return the operator_sum as a string. std::string to_string() const; @@ -248,7 +212,21 @@ class operator_sum { const std::map &dimensions, const std::map> ¶ms = {}) const; - // Arithmetic operators + // comparisons + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms `blockwise`; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(const operator_sum &other) const; + + // right-hand arithmetics + operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; @@ -285,59 +263,6 @@ class operator_sum { operator_sum& operator*=(const operator_sum &other); operator_sum& operator+=(const operator_sum &other); operator_sum& operator-=(const operator_sum &other); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-template-friend" -#endif -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-template-friend" -#endif -/* - friend operator_sum operator*(double other, const operator_sum &self); - friend operator_sum operator+(double other, const operator_sum &self); - friend operator_sum operator-(double other, const operator_sum &self); - friend operator_sum operator*(std::complex other, const operator_sum &self); - friend operator_sum operator+(std::complex other, const operator_sum &self); - friend operator_sum operator-(std::complex other, const operator_sum &self); - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); -*/ -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic pop -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - /// @brief Return the number of operator terms that make up this operator sum. - int term_count() const { return terms.size(); } - - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. - bool operator==(const operator_sum &other) const; - - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: - std::vector> get_terms() const { - std::vector> prods; - prods.reserve(terms.size()); - for (size_t i = 0; i < terms.size(); ++i) { - prods.push_back(product_operator(coefficients[i], terms[i])); - } - return prods; } }; /// @brief Represents an operator expression consisting of a product of @@ -428,7 +353,7 @@ class product_operator : public operator_sum { /// @brief Return the number of operator terms that make up this product /// operator. - int term_count() const { return operator_sum::terms[0].size(); } + int n_terms() const { return operator_sum::terms[0].size(); } /// @brief Return the `product_operator` as a string. std::string to_string() const; diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 53556ce83a..57bd633e40 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -136,8 +136,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto reverse = other + self; // Check the `operator_sum` attributes. - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -163,8 +163,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto sum = self + other; auto reverse = other + self; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -191,8 +191,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -218,8 +218,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -306,7 +306,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self + other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -324,7 +324,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self + other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -346,7 +346,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self - other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -364,7 +364,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self - other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -386,7 +386,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `product_operator` type. auto product = self * other; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -404,7 +404,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `product_operator` type. auto product = self * other; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -438,8 +438,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto got = self + operator_sum; auto reverse = operator_sum + self; - ASSERT_TRUE(got.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -471,8 +471,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto got = self - operator_sum; auto reverse = operator_sum - self; - ASSERT_TRUE(got.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -504,14 +504,14 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto got = self * operator_sum; auto reverse = operator_sum * self; - ASSERT_TRUE(got.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(got.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto &term : got.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); for (auto &term : reverse.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -539,7 +539,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { cudaq::elementary_operator::identity(1); operator_sum += cudaq::elementary_operator::annihilate(0); - ASSERT_TRUE(operator_sum.term_count() == 3); + ASSERT_TRUE(operator_sum.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -563,7 +563,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { cudaq::elementary_operator::identity(1); operator_sum -= cudaq::elementary_operator::annihilate(0); - ASSERT_TRUE(operator_sum.term_count() == 3); + ASSERT_TRUE(operator_sum.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -590,10 +590,10 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { operator_sum *= self; - ASSERT_TRUE(operator_sum.term_count() == 2); + ASSERT_TRUE(operator_sum.n_terms() == 2); for (auto &term : operator_sum.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 4ae7c55b74..282ddd46d6 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -237,16 +237,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto product = sum * cudaq::scalar_operator(0.1); auto reverse = cudaq::scalar_operator(0.1) * sum; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -259,8 +259,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto sum = original + cudaq::scalar_operator(1.0); auto reverse = cudaq::scalar_operator(1.0) + original; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -271,8 +271,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto difference = original - cudaq::scalar_operator(1.0); auto reverse = cudaq::scalar_operator(1.0) - original; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum *= scalar_operator` @@ -282,9 +282,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { sum *= cudaq::scalar_operator(0.1); - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -296,7 +296,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { sum += cudaq::scalar_operator(1.0); - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= scalar_operator` @@ -306,7 +306,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { sum -= cudaq::scalar_operator(1.0); - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } } @@ -321,16 +321,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * 2.0; auto reverse = 2.0 * sum; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } } @@ -343,8 +343,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + 2.0; auto reverse = 2.0 + original; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum - double` and `double - operator_sum` @@ -355,8 +355,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - 2.0; auto reverse = 2.0 - original; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum *= double` @@ -366,9 +366,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= 2.0; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); std::cout << "GOT: " << term.get_coefficient().evaluate() << std::endl; ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } @@ -381,7 +381,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += 2.0; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= double` @@ -391,7 +391,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= 2.0; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum * std::complex` and `std::complex * @@ -403,16 +403,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * value; auto reverse = value * sum; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } @@ -426,8 +426,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + value; auto reverse = value + original; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum - std::complex` and `std::complex - @@ -439,8 +439,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - value; auto reverse = value - original; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum *= std::complex` @@ -450,9 +450,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= value; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } @@ -464,7 +464,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += value; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= std::complex` @@ -474,7 +474,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= value; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } } @@ -489,7 +489,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto sum = sum_0 + sum_1; - ASSERT_TRUE(sum.term_count() == 5); + ASSERT_TRUE(sum.n_terms() == 5); } // `operator_sum - operator_sum` @@ -502,7 +502,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto difference = sum_0 - sum_1; - ASSERT_TRUE(difference.term_count() == 5); + ASSERT_TRUE(difference.n_terms() == 5); } // `operator_sum * operator_sum` @@ -515,9 +515,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto sum_product = sum_0 * sum_1; - ASSERT_TRUE(sum_product.term_count() == 6); + ASSERT_TRUE(sum_product.n_terms() == 6); for (auto term : sum_product.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); } // `operator_sum *= operator_sum` @@ -530,9 +530,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { sum *= sum_1; - ASSERT_TRUE(sum.term_count() == 6); + ASSERT_TRUE(sum.n_terms() == 6); for (auto term : sum.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); } } @@ -549,7 +549,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum += product; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= product_operator` @@ -561,7 +561,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum -= product; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum *= product_operator` @@ -573,10 +573,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum *= product; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 3); + ASSERT_TRUE(term.n_terms() == 3); } } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 2a585e86a6..9b399be7f1 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -255,8 +255,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = value_0 + product_op; auto reverse = product_op + value_0; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); @@ -271,8 +271,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = 2.0 + product_op; auto reverse = product_op + 2.0; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); @@ -288,8 +288,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = scalar_op + product_op; auto reverse = product_op + scalar_op; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); @@ -304,8 +304,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = value_0 - product_op; auto reverse = product_op - value_0; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); @@ -320,8 +320,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = 2.0 - product_op; auto reverse = product_op - 2.0; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); @@ -337,8 +337,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = scalar_op - product_op; auto reverse = product_op - scalar_op; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); @@ -350,14 +350,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); @@ -371,14 +371,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); @@ -392,15 +392,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); @@ -415,7 +415,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= value_0; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; @@ -428,7 +428,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= 2.0; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; @@ -442,7 +442,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto scalar_op = cudaq::scalar_operator(0.1); product *= scalar_op; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(scalar_op.evaluate() == std::complex(0.1)); @@ -462,7 +462,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto sum = term_0 + term_1; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); } // `product_operator - product_operator` @@ -474,7 +474,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto difference = term_0 - term_1; - ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); } // `product_operator * product_operator` @@ -486,7 +486,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto product = term_0 * term_1; - ASSERT_TRUE(product.term_count() == 4); + ASSERT_TRUE(product.n_terms() == 4); } // `product_operator *= product_operator` @@ -498,7 +498,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { term_0 *= term_1; - ASSERT_TRUE(term_0.term_count() == 4); + ASSERT_TRUE(term_0.n_terms() == 4); } } @@ -513,8 +513,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto sum = product + elementary; auto reverse = elementary + product; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); } // `product_operator - elementary_operator` @@ -526,8 +526,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto difference = product - elementary; auto reverse = elementary - product; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); } // `product_operator * elementary_operator` @@ -539,8 +539,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto product = term_0 * elementary; auto reverse = elementary * term_0; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `product_operator *= elementary_operator` @@ -551,7 +551,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { product *= elementary; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.n_terms() == 3); } } @@ -567,8 +567,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto sum = product + original_sum; auto reverse = original_sum + product; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `product_operator - operator_sum` @@ -581,8 +581,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto difference = product - original_sum; auto reverse = original_sum - product; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `product_operator * operator_sum` @@ -595,15 +595,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto product = original_product * sum; auto reverse = sum * original_product; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 3); + ASSERT_TRUE(term.n_terms() == 3); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 3); + ASSERT_TRUE(term.n_terms() == 3); } } } From 79a51b3ec07b814ec70b3367f32766f941e4dae6 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 28 Jan 2025 18:59:47 +0000 Subject: [PATCH 064/311] some clean up for products Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 7 +- runtime/cudaq/dynamics/product_operators.cpp | 326 +++++++++++-------- runtime/cudaq/operators.h | 167 +++------- 3 files changed, 242 insertions(+), 258 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 4ea1d3dcde..ad3ec284b3 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -32,10 +32,7 @@ std::vector> operator_sum::_ca template requires std::derived_from -void operator_sum::aggregate_terms(const product_operator &head) { - this->terms.push_back(head.terms[0]); - this->coefficients.push_back(head.coefficients[0]); -} +void operator_sum::aggregate_terms() {} template requires std::derived_from @@ -52,6 +49,8 @@ std::vector> operator_sum> operator_sum::_canonical_terms() const; +// no constructor for a single product, since that one should remain a product op + template void operator_sum::aggregate_terms(const product_operator &item1, const product_operator &item2); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 53fc7acaf4..458157ed13 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -16,153 +16,205 @@ namespace cudaq { -// tensor kroneckerHelper(std::vector &matrices) { -// // essentially we pass in the list of elementary operators to -// // this function -- with lowest degree being leftmost -- then it computes -// the -// // kronecker product of all of them. -// auto kronecker = [](tensor self, tensor other) { -// return self.kronecker(other); -// }; - -// return std::accumulate(begin(matrices), end(matrices), -// tensor::identity(1, 1), kronecker); -// } - -matrix_2 product_operator::to_matrix( - const std::map dimensions, - const std::map> parameters) const { - // Lambda functions to retrieve degrees and matrices - auto getDegrees = [](auto &&term) { return term.degrees; }; - auto getMatrix = [&](auto &&term) { - return term.to_matrix(dimensions, parameters); - }; - - // Initialize a result matrix with a single identity element - matrix_2 result(1, 1); - result[{0, 0}] = 1.0; - - // Iterate over all terms in the product operator - for (const auto &term : m_terms) { - // Get the degrees for the current term - auto termDegrees = std::visit(getDegrees, term); - bool inserted = false; - - matrix_2 termMatrix(1, 1); - termMatrix[{0, 0}] = 1.0; - - // Build the matrix list with identities or operator matrices - for (const auto &[degree, dim] : dimensions) { - if (std::find(termDegrees.begin(), termDegrees.end(), degree) != - termDegrees.end() && - !inserted) { - // Use the operator matrix for the active degree - termMatrix.kronecker_inplace(std::visit(getMatrix, term)); - inserted = true; - } else { - // Use identity matrix for other degrees - matrix_2 identityMatrix(dim, dim); - for (std::size_t i = 0; i < dim; i++) { - identityMatrix[{i, i}] = 1.0; - } - termMatrix.kronecker_inplace(identityMatrix); - } - } - - // Multiply the result matrix by the term matrix - result *= termMatrix; +// private methods + +template +requires std::derived_from +void product_operator::aggregate_terms() {} + +template +requires std::derived_from +template +void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { + this->terms[0].push_back(head); + aggregate_terms(std::forward(args)...); +} + +template +void product_operator::aggregate_terms(const elementary_operator &item1, + const elementary_operator &item2); + +template +void product_operator::aggregate_terms(const elementary_operator &item1, + const elementary_operator &item2, + const elementary_operator &item3); + +// read-only properties + +template +requires std::derived_from +std::vector product_operator::degrees() const { + std::set unsorted_degrees; + for (const HandlerTy &term : this->terms[0]) { + unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); } + auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION + return degrees; +} + +template +requires std::derived_from +int product_operator::n_terms() const { + return this->terms[0].size(); +} + +template +requires std::derived_from +std::vector product_operator::get_terms() const { + return this->terms[0]; +} + +template +requires std::derived_from +scalar_operator product_operator::get_coefficient() const { + return this->coefficients[0]; +} + +template +std::vector product_operator::degrees() const; + +template +int product_operator::n_terms() const; + +template +std::vector product_operator::get_terms() const; + +template +scalar_operator product_operator::get_coefficient() const; + +// constructors + +template +requires std::derived_from +template +product_operator::product_operator(scalar_operator coefficient, const Args&... args) { + this->coefficients.push_back(std::move(coefficient)); + std::vector ops = {}; + ops.reserve(sizeof...(Args)); + this->terms.push_back(ops); + aggregate_terms(args...); +} + +template +requires std::derived_from +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) { + this->terms.push_back(atomic_operators); + this->coefficients.push_back(std::move(coefficient)); +} + +template +requires std::derived_from +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) { + this->terms.push_back(std::move(atomic_operators)); + this->coefficients.push_back(std::move(coefficient)); +} + +template +requires std::derived_from +product_operator::product_operator(const product_operator &other) { + this->terms = other.terms; + this->coefficients = other.coefficients; +} - return result; +template +requires std::derived_from +product_operator::product_operator(product_operator &&other) { + this->terms = std::move(other.terms); + this->coefficients = std::move(other.coefficients); } -// /// IMPLEMENT: -// tensor product_operator::to_matrix( -// std::map dimensions, -// std::map> parameters) { - -// /// TODO: This initial logic may not be needed. -// // std::vector degrees, levels; -// // for(std::map::iterator it = dimensions.begin(); it != -// // dimensions.end(); ++it) { -// // degrees.push_back(it->first); -// // levels.push_back(it->second); -// // } -// // // Calculate the size of the full Hilbert space of the given product -// // operator. int fullSize = std::accumulate(begin(levels), end(levels), 1, -// // std::multiplies()); -// std::cout << "here 49\n"; -// auto getDegrees = [](auto &&t) { return t.degrees; }; -// auto getMatrix = [&](auto &&t) { -// auto outMatrix = t.to_matrix(dimensions, parameters); -// std::cout << "dumping the outMatrix : \n"; -// outMatrix.dump(); -// return outMatrix; -// }; -// std::vector matricesFullVectorSpace; -// for (auto &term : ops) { -// auto op_degrees = std::visit(getDegrees, term); -// std::cout << "here 58\n"; -// // Keeps track of if we've already inserted the operator matrix -// // into the full list of matrices. -// bool alreadyInserted = false; -// std::vector matrixWithIdentities; -// /// General procedure for inserting identities: -// // * check if the operator acts on this degree by looking through -// // `op_degrees` -// // * if not, insert an identity matrix of the proper level size -// // * if so, insert the matrix itself -// for (auto [degree, level] : dimensions) { -// std::cout << "here 68\n"; -// auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); -// if (it != op_degrees.end() && !alreadyInserted) { -// std::cout << "here 71\n"; -// auto matrix = std::visit(getMatrix, term); -// std::cout << "here 75\n"; -// matrixWithIdentities.push_back(matrix); -// std::cout << "here 77\n"; -// } else { -// std::cout << "here 80\n"; -// matrixWithIdentities.push_back(tensor::identity(level, -// level)); -// } -// } -// std::cout << "here 84\n"; -// matricesFullVectorSpace.push_back(kroneckerHelper(matrixWithIdentities)); -// } -// // Now just need to accumulate with matrix multiplication all of the -// // matrices in `matricesFullVectorSpace` -- they should all be the same -// size -// // already. -// std::cout << "here 89\n"; - -// // temporary -// auto out = tensor::identity(1, 1); -// std::cout << "here 93\n"; -// return out; -// } - - -// FIXME: remove - to be replaced with the general implementation for product op -template<> -matrix_2 product_operator::to_matrix( - std::map dimensions, - std::map> parameters) { +template +product_operator::product_operator(scalar_operator coefficient); + +template +product_operator::product_operator(scalar_operator coefficient, + const elementary_operator &item1); + +template +product_operator::product_operator(scalar_operator coefficient, + const elementary_operator &item1, + const elementary_operator &item2); + +template +product_operator::product_operator(scalar_operator coefficient, + const elementary_operator &item1, + const elementary_operator &item2, + const elementary_operator &item3); + +template +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + +template +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + +template +product_operator::product_operator(const product_operator &other); + +template +product_operator::product_operator(product_operator &&other); + +// assignments + +template +requires std::derived_from +product_operator& product_operator::operator=(const product_operator &other) { + if (this != &other) { + this->terms = other.terms; + this->coefficients = other.coefficients; + } + return *this; +} + +template +requires std::derived_from +product_operator& product_operator::operator=(product_operator &&other) { + if (this != &other) { + this->coefficients = std::move(other.coefficients); + this->terms = std::move(other.terms); + } + return *this; +} + +template +product_operator& product_operator::operator=(const product_operator &other); + +template +product_operator& product_operator::operator=(product_operator &&other); + +// evaluations + +template +requires std::derived_from +std::string product_operator::to_string() const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +matrix_2 product_operator::to_matrix(std::map dimensions, + std::map> parameters) const { if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) throw std::runtime_error("not implemented"); return this->get_terms()[0].to_matrix(dimensions, parameters); } +template +std::string product_operator::to_string() const; -// Degrees property -template -std::vector product_operator::degrees() const { - std::set unique_degrees; - for (const HandlerTy &term : this->get_terms()) { - unique_degrees.insert(term.degrees.begin(), term.degrees.end()); - } - // FIXME: SORT THE DEGREES - return std::vector(unique_degrees.begin(), unique_degrees.end()); +template +matrix_2 product_operator::to_matrix(std::map dimensions, + std::map> parameters) const; + +// comparisons + +template +requires std::derived_from +bool product_operator::operator==(const product_operator &other) const { + throw std::runtime_error("not implemented"); } +template +bool product_operator::operator==(const product_operator &other) const; + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 68b78c0a9c..02337caabb 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -147,7 +147,7 @@ class operator_sum { std::vector> _canonical_terms() const; - void aggregate_terms(const product_operator& head); + void aggregate_terms(); template void aggregate_terms(const product_operator &head, Args&& ... args); @@ -208,9 +208,8 @@ class operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix( - const std::map &dimensions, - const std::map> ¶ms = {}) const; + matrix_2 to_matrix(const std::map &dimensions = {}, + const std::map ¶ms = {}) const; // comparisons @@ -265,6 +264,7 @@ class operator_sum { operator_sum& operator-=(const operator_sum &other); }; + /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types @@ -275,85 +275,53 @@ class product_operator : public operator_sum { private: - void aggregate_terms(const HandlerTy& head) { - operator_sum::terms[0].push_back(head); - } + void aggregate_terms(); template - void aggregate_terms(const HandlerTy &head, Args&& ... args) { - operator_sum::terms[0].push_back(head); - aggregate_terms(std::forward(args)...); - } + void aggregate_terms(const HandlerTy &head, Args&& ... args); public: - product_operator(scalar_operator coefficient) { - operator_sum::terms.push_back({}); - operator_sum::coefficients.push_back(std::move(coefficient)); - } + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + /// @brief Return the number of operator terms that make up this product + /// operator. + int n_terms() const; + + std::vector get_terms() const; + + scalar_operator get_coefficient() const; + + // constructors and destructors - // Constructor for an operator expression that represents a product - // of scalar and elementary operators. - // arg atomic_operators : The operators of which to compute the product when - // evaluating the operator expression. template...>::value, void>> - product_operator(scalar_operator coefficient, const Args&... args) { - operator_sum::coefficients.push_back(std::move(coefficient)); - std::vector ops = {}; - ops.reserve(sizeof...(Args)); - operator_sum::terms.push_back(ops); - aggregate_terms(args...); - } + product_operator(scalar_operator coefficient, const Args&... args); - product_operator(scalar_operator coefficient, const std::vector& atomic_operators) { - operator_sum::terms.push_back(atomic_operators); - operator_sum::coefficients.push_back(std::move(coefficient)); - } + product_operator(scalar_operator coefficient, const std::vector &atomic_operators); - product_operator(scalar_operator coefficient, std::vector&& atomic_operators) { - operator_sum::terms.push_back(std::move(atomic_operators)); - operator_sum::coefficients.push_back(std::move(coefficient)); - } + product_operator(scalar_operator coefficient, std::vector &&atomic_operators); // copy constructor - product_operator(const product_operator &other) { - operator_sum::terms = other.terms; - operator_sum::coefficients = other.coefficients; - } + product_operator(const product_operator &other); // move constructor - product_operator(product_operator &&other) { - operator_sum::terms = std::move(other.terms); - operator_sum::coefficients = std::move(other.coefficients); - } + product_operator(product_operator &&other); - // assignment operator - product_operator& operator=(const product_operator& other) { - if (this != &other) { - operator_sum::terms = other.terms; - operator_sum::coefficients = other.coefficients; - } - return *this; - } + ~product_operator() = default; - // move assignment operator - product_operator& operator=(product_operator &&other) { - if (this != &other) { - this->coefficients = std::move(other.coefficients); - this->terms = std::move(other.terms); - } - return *this; - } + // assignments - ~product_operator() = default; + // assignment operator + product_operator& operator=(const product_operator &other); - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees() const; + // move assignment operator + product_operator& operator=(product_operator &&other); - /// @brief Return the number of operator terms that make up this product - /// operator. - int n_terms() const { return operator_sum::terms[0].size(); } + // evaluations /// @brief Return the `product_operator` as a string. std::string to_string() const; @@ -365,11 +333,24 @@ class product_operator : public operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 - to_matrix(const std::map dimensions, - const std::map> parameters) const; + matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; + + // comparisons + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms `blockwise`; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(const product_operator &other) const; + + // right-hand arithmetics - // Arithmetic overloads against all other operator types. product_operator operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; @@ -393,54 +374,6 @@ class product_operator : public operator_sum { operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-template-friend" -#endif -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-template-friend" -#endif -/* - friend product_operator operator*(double other, const product_operator &self); - friend operator_sum operator+(double other, const product_operator &self); - friend operator_sum operator-(double other, const product_operator &self); - friend product_operator operator*(std::complex other, const product_operator &self); - friend operator_sum operator+(std::complex other, const product_operator &self); - friend operator_sum operator-(std::complex other, const product_operator &self); - friend product_operator operator*(const scalar_operator &other, const product_operator &self); - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - friend product_operator operator*(const HandlerTy &other, const product_operator &self); - friend operator_sum operator+(const HandlerTy &other, const product_operator &self); - friend operator_sum operator-(const HandlerTy &other, const product_operator &self); -*/ -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic pop -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. - bool operator==(product_operator other); - - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: - std::vector get_terms() const { - return operator_sum::terms[0]; } - - scalar_operator get_coefficient() const { - return operator_sum::coefficients[0]; } }; From 39fd8c26ec8cf17033f190929bc5805774ab1db2 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 11:08:01 +0000 Subject: [PATCH 065/311] no type constraints for HandlerTy Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 2 +- .../{instantiations.cpp => arithmetics.cpp} | 87 +------------------ ...{template_declarations.h => arithmetics.h} | 26 ------ runtime/cudaq/dynamics/operator_sum.cpp | 17 ---- runtime/cudaq/dynamics/product_operators.cpp | 16 ---- runtime/cudaq/operators.h | 4 +- 6 files changed, 4 insertions(+), 148 deletions(-) rename runtime/cudaq/dynamics/{instantiations.cpp => arithmetics.cpp} (85%) rename runtime/cudaq/dynamics/{template_declarations.h => arithmetics.h} (82%) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 3bbfb4eec0..45bedd7d65 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -22,7 +22,7 @@ set(CUDAQ_OPS_SRC elementary_operators.cpp product_operators.cpp operator_sum.cpp - instantiations.cpp + arithmetics.cpp schedule.cpp ) diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/arithmetics.cpp similarity index 85% rename from runtime/cudaq/dynamics/instantiations.cpp rename to runtime/cudaq/dynamics/arithmetics.cpp index 77741ac1a9..609575e502 100644 --- a/runtime/cudaq/dynamics/instantiations.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -20,61 +20,51 @@ namespace cudaq { // product operator left-hand arithmetics template -requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self) { return product_operator(other * std::move(self.get_coefficient()), std::move(self.get_terms())); } template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator({other}), self); + return operator_sum(product_operator(other), self); } template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator({other}), self * (-1.)); + return operator_sum(product_operator(other), self * (-1.)); } template -requires std::derived_from product_operator operator*(double other, const product_operator &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(double other, const product_operator &self) { return operator_sum(product_operator({scalar_operator(other)}), self); } template -requires std::derived_from operator_sum operator-(double other, const product_operator &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from product_operator operator*(const std::complex other, const product_operator &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(const std::complex other, const product_operator &self) { return operator_sum(product_operator({scalar_operator(other)}), self); } template -requires std::derived_from operator_sum operator-(const std::complex other, const product_operator &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self) { std::vector terms = std::move(self.get_terms()); terms.insert(terms.begin(), other); @@ -82,13 +72,11 @@ product_operator operator*(const HandlerTy &other, const product_oper } template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self) { return operator_sum(product_operator(1., other), self); } template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const product_operator &self) { return (self * (-1.)) + other; } @@ -96,7 +84,6 @@ operator_sum operator-(const HandlerTy &other, const product_operator // operator sum left-hand arithmetics template -requires std::derived_from operator_sum operator*(const scalar_operator &other, const operator_sum &self) { std::vector> terms = self.get_terms(); for (auto &term : terms) @@ -105,7 +92,6 @@ operator_sum operator*(const scalar_operator &other, const operator_s } template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self) { std::vector> terms = self.get_terms(); terms.insert(terms.begin(), product_operator(other)); @@ -113,7 +99,6 @@ operator_sum operator+(const scalar_operator &other, const operator_s } template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self) { auto negative_self = self * (-1.); std::vector> terms = negative_self.get_terms(); @@ -122,55 +107,46 @@ operator_sum operator-(const scalar_operator &other, const operator_s } template -requires std::derived_from operator_sum operator*(double other, const operator_sum &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(double other, const operator_sum &self) { return self + scalar_operator(other); } template -requires std::derived_from operator_sum operator-(double other, const operator_sum &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from operator_sum operator*(std::complex other, const operator_sum &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(std::complex other, const operator_sum &self) { return self + scalar_operator(other); } template -requires std::derived_from operator_sum operator-(std::complex other, const operator_sum &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self) { return ((operator_sum)product_operator(1., other)) * self; } template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self) { return ((operator_sum)product_operator(1., other)) + self; } template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self) { return ((operator_sum)product_operator(1., other)) - self; } @@ -178,85 +154,72 @@ operator_sum operator-(const HandlerTy &other, const operator_sum -requires std::derived_from operator_sum operator_sum::operator*(double other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator+(double other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator-(double other) const { return *this - scalar_operator(other); } template -requires std::derived_from operator_sum& operator_sum::operator*=(double other) { *this *= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(double other) { *this += scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(double other) { *this -= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(std::complex other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator+(std::complex other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator-(std::complex other) const { return *this - scalar_operator(other); } template -requires std::derived_from operator_sum& operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(std::complex other) { *this += scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(std::complex other) { *this -= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(const scalar_operator &other) const { std::vector> combined_terms = std::move(this->get_terms()); for (auto &term : combined_terms) { @@ -266,7 +229,6 @@ operator_sum operator_sum::operator*(const scalar_operator } template -requires std::derived_from operator_sum operator_sum::operator+(const scalar_operator &other) const { // FIXME: reserve length auto combined_terms = std::move(this->get_terms()); @@ -275,34 +237,29 @@ operator_sum operator_sum::operator+(const scalar_operator } template -requires std::derived_from operator_sum operator_sum::operator-(const scalar_operator &other) const { return *this + (other * (-1.0)); } template -requires std::derived_from operator_sum& operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const scalar_operator &other) { *this = *this + other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(const scalar_operator &other) { *this = *this - other; return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(const HandlerTy &other) const { std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { @@ -312,7 +269,6 @@ operator_sum operator_sum::operator*(const HandlerTy &othe } template -requires std::derived_from operator_sum operator_sum::operator+(const HandlerTy &other) const { auto combined_terms = std::move(this->get_terms()); combined_terms.push_back(product_operator(1., other)); @@ -320,7 +276,6 @@ operator_sum operator_sum::operator+(const HandlerTy &othe } template -requires std::derived_from operator_sum operator_sum::operator-(const HandlerTy &other) const { std::vector> combined_terms = std::move(this->get_terms()); combined_terms.push_back(product_operator(-1., other)); @@ -328,14 +283,12 @@ operator_sum operator_sum::operator-(const HandlerTy &othe } template -requires std::derived_from operator_sum& operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const HandlerTy &other) { this->coefficients.push_back(1.); this->terms.push_back({other}); @@ -343,7 +296,6 @@ operator_sum& operator_sum::operator+=(const HandlerTy &ot } template -requires std::derived_from operator_sum& operator_sum::operator-=(const HandlerTy &other) { this->coefficients.push_back(-1.); this->terms.push_back({other}); @@ -351,7 +303,6 @@ operator_sum& operator_sum::operator-=(const HandlerTy &ot } template -requires std::derived_from operator_sum operator_sum::operator*(const product_operator &other) const { std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { @@ -361,7 +312,6 @@ operator_sum operator_sum::operator*(const product_operato } template -requires std::derived_from operator_sum operator_sum::operator+(const product_operator &other) const { std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other); @@ -369,7 +319,6 @@ operator_sum operator_sum::operator+(const product_operato } template -requires std::derived_from operator_sum operator_sum::operator-(const product_operator &other) const { std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other * (-1.)); @@ -377,28 +326,24 @@ operator_sum operator_sum::operator-(const product_operato } template -requires std::derived_from operator_sum& operator_sum::operator*=(const product_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const product_operator &other) { *this = *this + other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(const product_operator &other) { *this = *this - other; return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(const operator_sum &other) const { auto self_terms = this->get_terms(); std::vector> product_terms; @@ -412,7 +357,6 @@ operator_sum operator_sum::operator*(const operator_sum -requires std::derived_from operator_sum operator_sum::operator+(const operator_sum &other) const { std::vector> combined_terms = std::move(this->get_terms()); std::vector> other_terms = std::move(other.get_terms()); @@ -421,27 +365,23 @@ operator_sum operator_sum::operator+(const operator_sum -requires std::derived_from operator_sum operator_sum::operator-(const operator_sum &other) const { return *this + (other * (-1)); } template -requires std::derived_from operator_sum& operator_sum::operator*=(const operator_sum &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(const operator_sum &other) { *this = *this - other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const operator_sum &other) { *this = *this + other; return *this; @@ -451,84 +391,71 @@ operator_sum& operator_sum::operator+=(const operator_sum< // product operator right-hand arithmetics template -requires std::derived_from product_operator product_operator::operator*(double other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator+(double other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator-(double other) const { return *this - scalar_operator(other); } template -requires std::derived_from product_operator& product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } template -requires std::derived_from product_operator product_operator::operator*(std::complex other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator+(std::complex other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator-(std::complex other) const { return *this - scalar_operator(other); } template -requires std::derived_from product_operator& product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } template -requires std::derived_from product_operator product_operator::operator*(const scalar_operator &other) const { return product_operator(this->coefficients[0] * other, this->terms[0]); } template -requires std::derived_from operator_sum product_operator::operator+(const scalar_operator &other) const { product_operator coefficient(other); return operator_sum(coefficient, *this); } template -requires std::derived_from operator_sum product_operator::operator-(const scalar_operator &other) const { product_operator coefficient(-1. * other); return operator_sum(coefficient, *this); } template -requires std::derived_from product_operator& product_operator::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from product_operator product_operator::operator*(const HandlerTy &other) const { auto combined_terms = this->terms[0]; combined_terms.push_back(other); @@ -536,26 +463,22 @@ product_operator product_operator::operator*(const Handler } template -requires std::derived_from operator_sum product_operator::operator+(const HandlerTy &other) const { return operator_sum(*this, product_operator(1., other)); } template -requires std::derived_from operator_sum product_operator::operator-(const HandlerTy &other) const { return operator_sum(*this, product_operator(-1., other)); } template -requires std::derived_from product_operator& product_operator::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } template -requires std::derived_from product_operator product_operator::operator*(const product_operator &other) const { auto combined_terms = this->terms[0]; combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); @@ -563,26 +486,22 @@ product_operator product_operator::operator*(const product } template -requires std::derived_from operator_sum product_operator::operator+(const product_operator &other) const { return operator_sum(*this, other); } template -requires std::derived_from operator_sum product_operator::operator-(const product_operator &other) const { return operator_sum(*this, other * (-1.)); } template -requires std::derived_from product_operator& product_operator::operator*=(const product_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum product_operator::operator*(const operator_sum &other) const { std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) { @@ -592,7 +511,6 @@ operator_sum product_operator::operator*(const operator_su } template -requires std::derived_from operator_sum product_operator::operator+(const operator_sum &other) const { std::vector other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); @@ -600,7 +518,6 @@ operator_sum product_operator::operator+(const operator_su } template -requires std::derived_from operator_sum product_operator::operator-(const operator_sum &other) const { auto negative_other = other * (-1.); std::vector> other_terms = negative_other.get_terms(); diff --git a/runtime/cudaq/dynamics/template_declarations.h b/runtime/cudaq/dynamics/arithmetics.h similarity index 82% rename from runtime/cudaq/dynamics/template_declarations.h rename to runtime/cudaq/dynamics/arithmetics.h index e1cec53736..8100562c9f 100644 --- a/runtime/cudaq/dynamics/template_declarations.h +++ b/runtime/cudaq/dynamics/arithmetics.h @@ -17,85 +17,59 @@ class scalar_operator; class elementary_operator; template -requires std::derived_from class product_operator; template -requires std::derived_from class operator_sum; template -requires std::derived_from product_operator operator*(double other, const product_operator &self); template -requires std::derived_from operator_sum operator+(double other, const product_operator &self); template -requires std::derived_from operator_sum operator-(double other, const product_operator &self); template -requires std::derived_from product_operator operator*(std::complex other, const product_operator &self); template -requires std::derived_from operator_sum operator+(std::complex other, const product_operator &self); template -requires std::derived_from operator_sum operator-(std::complex other, const product_operator &self); template -requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self); template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self); template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self); template -requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self); template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self); template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const product_operator &self); template -requires std::derived_from operator_sum operator*(double other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(double other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(double other, const operator_sum &self); template -requires std::derived_from operator_sum operator*(std::complex other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(std::complex other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(std::complex other, const operator_sum &self); template -requires std::derived_from operator_sum operator*(const scalar_operator &other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index ad3ec284b3..237b35e09b 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -19,23 +19,19 @@ namespace cudaq { // private methods template -requires std::derived_from std::vector> operator_sum::canonicalize_product(product_operator &prod) const { throw std::runtime_error("not implemented"); } template -requires std::derived_from std::vector> operator_sum::_canonical_terms() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from void operator_sum::aggregate_terms() {} template -requires std::derived_from template void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { this->terms.push_back(head.terms[0]); @@ -63,19 +59,16 @@ void operator_sum::aggregate_terms(const product_operator -requires std::derived_from std::vector operator_sum::degrees() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from int operator_sum::n_terms() const { return this->terms.size(); } template -requires std::derived_from std::vector> operator_sum::get_terms() const { std::vector> prods; prods.reserve(this->terms.size()); @@ -97,7 +90,6 @@ std::vector> operator_sum -requires std::derived_from template operator_sum::operator_sum(const Args&... args) { this->terms.reserve(sizeof...(Args)); @@ -106,7 +98,6 @@ operator_sum::operator_sum(const Args&... args) { } template -requires std::derived_from operator_sum::operator_sum(const std::vector> &terms) { this->terms.reserve(terms.size()); this->coefficients.reserve(terms.size()); @@ -117,7 +108,6 @@ operator_sum::operator_sum(const std::vector -requires std::derived_from operator_sum::operator_sum(std::vector> &&terms) { this->terms.reserve(terms.size()); for (const product_operator& term : terms) { @@ -127,12 +117,10 @@ operator_sum::operator_sum(std::vector> & } template -requires std::derived_from operator_sum::operator_sum(const operator_sum &other) : coefficients(other.coefficients), terms(other.terms) {} template -requires std::derived_from operator_sum::operator_sum(operator_sum &&other) : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} @@ -163,7 +151,6 @@ operator_sum::operator_sum(operator_sum -requires std::derived_from operator_sum& operator_sum::operator=(const operator_sum &other) { if (this != &other) { coefficients = other.coefficients; @@ -173,7 +160,6 @@ operator_sum& operator_sum::operator=(const operator_sum -requires std::derived_from operator_sum& operator_sum::operator=(operator_sum &&other) { if (this != &other) { coefficients = std::move(other.coefficients); @@ -191,13 +177,11 @@ operator_sum& operator_sum::operator=( // evaluations template -requires std::derived_from std::string operator_sum::to_string() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from matrix_2 operator_sum::to_matrix(const std::map &dimensions, const std::map ¶ms) const { throw std::runtime_error("not implemented"); @@ -213,7 +197,6 @@ matrix_2 operator_sum::to_matrix(const std::map & // comparisons template -requires std::derived_from bool operator_sum::operator==(const operator_sum &other) const { throw std::runtime_error("not implemented"); } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 458157ed13..fdebec00d0 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -19,11 +19,9 @@ namespace cudaq { // private methods template -requires std::derived_from void product_operator::aggregate_terms() {} template -requires std::derived_from template void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { this->terms[0].push_back(head); @@ -42,7 +40,6 @@ void product_operator::aggregate_terms(const elementary_ope // read-only properties template -requires std::derived_from std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->terms[0]) { @@ -54,19 +51,16 @@ std::vector product_operator::degrees() const { } template -requires std::derived_from int product_operator::n_terms() const { return this->terms[0].size(); } template -requires std::derived_from std::vector product_operator::get_terms() const { return this->terms[0]; } template -requires std::derived_from scalar_operator product_operator::get_coefficient() const { return this->coefficients[0]; } @@ -86,7 +80,6 @@ scalar_operator product_operator::get_coefficient() const; // constructors template -requires std::derived_from template product_operator::product_operator(scalar_operator coefficient, const Args&... args) { this->coefficients.push_back(std::move(coefficient)); @@ -97,28 +90,24 @@ product_operator::product_operator(scalar_operator coefficient, const } template -requires std::derived_from product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) { this->terms.push_back(atomic_operators); this->coefficients.push_back(std::move(coefficient)); } template -requires std::derived_from product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) { this->terms.push_back(std::move(atomic_operators)); this->coefficients.push_back(std::move(coefficient)); } template -requires std::derived_from product_operator::product_operator(const product_operator &other) { this->terms = other.terms; this->coefficients = other.coefficients; } template -requires std::derived_from product_operator::product_operator(product_operator &&other) { this->terms = std::move(other.terms); this->coefficients = std::move(other.coefficients); @@ -157,7 +146,6 @@ product_operator::product_operator(product_operator -requires std::derived_from product_operator& product_operator::operator=(const product_operator &other) { if (this != &other) { this->terms = other.terms; @@ -167,7 +155,6 @@ product_operator& product_operator::operator=(const produc } template -requires std::derived_from product_operator& product_operator::operator=(product_operator &&other) { if (this != &other) { this->coefficients = std::move(other.coefficients); @@ -185,13 +172,11 @@ product_operator& product_operator::op // evaluations template -requires std::derived_from std::string product_operator::to_string() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const { if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) @@ -209,7 +194,6 @@ matrix_2 product_operator::to_matrix(std::map dim // comparisons template -requires std::derived_from bool product_operator::operator==(const product_operator &other) const { throw std::runtime_error("not implemented"); } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 02337caabb..70c6decc45 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,7 +8,7 @@ #pragma once -#include "dynamics/template_declarations.h" +#include "dynamics/arithmetics.h" #include "definition.h" #include "utils/tensor.h" @@ -136,7 +136,6 @@ class scalar_operator { /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. template // handler needs to inherit from operation_handler -requires std::derived_from class operator_sum { private: @@ -270,7 +269,6 @@ class operator_sum { /// quantum kernels, but they provide methods to convert them to data types /// that can. template // handler needs to inherit from operation_handler -requires std::derived_from class product_operator : public operator_sum { private: From e5ffd5c20162a1a8fb5c974a72870067d8368a00 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 14:52:45 +0000 Subject: [PATCH 066/311] left hand arithmetics cleanup Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 184 ------------------ runtime/cudaq/dynamics/operator_sum.cpp | 145 +++++++++++++- runtime/cudaq/dynamics/product_operators.cpp | 90 +++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 10 + runtime/cudaq/operators.h | 74 +++++++ .../dynamics/elementary_ops_arithmetic.cpp | 2 +- 6 files changed, 317 insertions(+), 188 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index 609575e502..c481c45d25 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -17,140 +17,6 @@ namespace cudaq { -// product operator left-hand arithmetics - -template -product_operator operator*(const scalar_operator &other, const product_operator &self) { - return product_operator(other * std::move(self.get_coefficient()), std::move(self.get_terms())); -} - -template -operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator(other), self); -} - -template -operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator(other), self * (-1.)); -} - -template -product_operator operator*(double other, const product_operator &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(double other, const product_operator &self) { - return operator_sum(product_operator({scalar_operator(other)}), self); -} - -template -operator_sum operator-(double other, const product_operator &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -product_operator operator*(const std::complex other, const product_operator &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum(product_operator({scalar_operator(other)}), self); -} - -template -operator_sum operator-(const std::complex other, const product_operator &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector terms = std::move(self.get_terms()); - terms.insert(terms.begin(), other); - return product_operator(std::move(self.get_coefficient()), std::move(terms)); -} - -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(1., other), self); -} - -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return (self * (-1.)) + other; -} - -// operator sum left-hand arithmetics - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - for (auto &term : terms) - term = term * other; - return operator_sum(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), product_operator(other)); - return operator_sum(terms); -} - -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self) { - auto negative_self = self * (-1.); - std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), product_operator(other)); - return operator_sum(terms); -} - -template -operator_sum operator*(double other, const operator_sum &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(double other, const operator_sum &self) { - return self + scalar_operator(other); -} - -template -operator_sum operator-(double other, const operator_sum &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -operator_sum operator*(std::complex other, const operator_sum &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(std::complex other, const operator_sum &self) { - return self + scalar_operator(other); -} - -template -operator_sum operator-(std::complex other, const operator_sum &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - return ((operator_sum)product_operator(1., other)) * self; -} - -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - return ((operator_sum)product_operator(1., other)) + self; -} - -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - return ((operator_sum)product_operator(1., other)) - self; -} - // operator sum right-hand arithmetics template @@ -528,56 +394,6 @@ operator_sum product_operator::operator-(const operator_su // instantiations -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -product_operator operator*(const elementary_operator &other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator+(const elementary_operator &other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -operator_sum operator-(const elementary_operator &other, const product_operator &self); - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); - template operator_sum operator_sum::operator*(double other) const; template diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 237b35e09b..23448cb5a8 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -45,7 +45,7 @@ std::vector> operator_sum> operator_sum::_canonical_terms() const; -// no constructor for a single product, since that one should remain a product op +// no overload for a single product, since we don't want a constructor for a single term template void operator_sum::aggregate_terms(const product_operator &item1, @@ -124,8 +124,7 @@ template operator_sum::operator_sum(operator_sum &&other) : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} -template -operator_sum::operator_sum(const product_operator &item1); +// no constructor for a single product, since that one should remain a product op template operator_sum::operator_sum(const product_operator &item1, @@ -204,4 +203,144 @@ bool operator_sum::operator==(const operator_sum &other) c template bool operator_sum::operator==(const operator_sum &other) const; +// unary operators + +template +operator_sum operator_sum::operator-() const { + std::vector coefficients; + coefficients.reserve(this->coefficients.size()); + for (auto coeff : this->coefficients) + coefficients.push_back(-1. * coeff); + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = this->terms; + return sum; +} + +template +operator_sum operator_sum::operator+() const { + return *this; +} + +template +operator_sum operator_sum::operator-() const; + +template +operator_sum operator_sum::operator+() const; + +// left-hand arithmetics + +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.coefficients.size()); \ + for (auto coeff : self.coefficients) \ + coefficients.push_back(coeff * other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = self.terms; \ + return sum; \ + } + +SUM_MULTIPLICATION_REVERSE(double); +SUM_MULTIPLICATION_REVERSE(std::complex); +SUM_MULTIPLICATION_REVERSE(const scalar_operator &); + +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(other); \ + for (auto coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + terms.push_back({}); \ + for (auto term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_REVERSE(double, +); +SUM_ADDITION_REVERSE(double, -); +SUM_ADDITION_REVERSE(std::complex, +); +SUM_ADDITION_REVERSE(std::complex, -); +SUM_ADDITION_REVERSE(const scalar_operator &, +); +SUM_ADDITION_REVERSE(const scalar_operator &, -); + +template +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> terms; + terms.reserve(self.terms.size()); + for (auto term : self.terms) { + std::vector prod; + prod.reserve(term.size() + 1); + prod.push_back(other); + for (auto op : term) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = self.coefficients; + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy & other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(1.); \ + for (auto coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_HANDLER_REVERSE(+) +SUM_ADDITION_HANDLER_REVERSE(-) + +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); +template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); +template +operator_sum operator-(std::complex other, const operator_sum &self); +template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index fdebec00d0..5182409045 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -201,4 +201,94 @@ bool product_operator::operator==(const product_operator & template bool product_operator::operator==(const product_operator &other) const; +// unary operators + +template +product_operator product_operator::operator-() const { + return product_operator(-1. * this->coefficients[0], this->terms[0]); +} + +template +product_operator product_operator::operator+() const { + return *this; +} + +template +product_operator product_operator::operator-() const; + +template +product_operator product_operator::operator+() const; + +// left-hand arithmetics + +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*(otherTy other, \ + const product_operator &self) { \ + return product_operator(other * self.coefficients[0], self.terms[0]); \ + } + +PRODUCT_MULTIPLICATION_REVERSE(double); +PRODUCT_MULTIPLICATION_REVERSE(std::complex); +PRODUCT_MULTIPLICATION_REVERSE(const scalar_operator &); + +#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const product_operator &self) { \ + return operator_sum(product_operator(other), op self); \ + } + +PRODUCT_ADDITION_REVERSE(double, +); +PRODUCT_ADDITION_REVERSE(double, -); +PRODUCT_ADDITION_REVERSE(std::complex, +); +PRODUCT_ADDITION_REVERSE(std::complex, -); +PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); +PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); + +template +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector terms; + terms.reserve(self.terms[0].size() + 1); + terms.push_back(other); + for (auto term : self.terms[0]) + terms.push_back(term); + return product_operator(self.coefficients[0], std::move(terms)); +} + +template +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + return operator_sum(product_operator(1., other), self); +} + +template +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return operator_sum(product_operator(1., other), -self); +} + +template +product_operator operator*(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); +template +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(const elementary_operator &other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator+(std::complex other, const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +operator_sum operator-(std::complex other, const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index f453fbf62f..42fac4e7e6 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -82,6 +82,16 @@ bool scalar_operator::operator==(scalar_operator other) { } } +// unary operators + +scalar_operator scalar_operator::operator-() const { + return *this * (-1.); +} + +scalar_operator scalar_operator::operator+() const { + return *this; +} + // right-hand arithmetics #define ARITHMETIC_OPERATIONS(op, otherTy) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 70c6decc45..3f248bf407 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -19,6 +19,9 @@ #include #include +// FIXME: DO WE REALLY NEED THE HANDLERTY OVERLOADS? -> EVERYTHING SHOULD BE A PRODUCT +// might be perf relevant to avoid the creation of unnecessary products... + namespace cudaq { class scalar_operator { @@ -81,6 +84,11 @@ class scalar_operator { bool operator==(scalar_operator other); + // unary operators + + scalar_operator operator-() const; + scalar_operator operator+() const; + // right-hand arithmetics scalar_operator operator*(double other) const; @@ -223,6 +231,11 @@ class operator_sum { /// are guaranteed to represent the same transformation for all arguments. bool operator==(const operator_sum &other) const; + // unary operators + + operator_sum operator-() const; + operator_sum operator+() const; + // right-hand arithmetics operator_sum operator*(double other) const; @@ -261,6 +274,34 @@ class operator_sum { operator_sum& operator*=(const operator_sum &other); operator_sum& operator+=(const operator_sum &other); operator_sum& operator-=(const operator_sum &other); + + // left-hand arithmetics + + // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. + template + friend operator_sum operator*(double other, const operator_sum &self); + template + friend operator_sum operator+(double other, const operator_sum &self); + template + friend operator_sum operator-(double other, const operator_sum &self); + template + friend operator_sum operator*(std::complex other, const operator_sum &self); + template + friend operator_sum operator+(std::complex other, const operator_sum &self); + template + friend operator_sum operator-(std::complex other, const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator*(const T &other, const operator_sum &self); + template + friend operator_sum operator+(const T &other, const operator_sum &self); + template + friend operator_sum operator-(const T &other, const operator_sum &self); }; @@ -347,6 +388,11 @@ class product_operator : public operator_sum { /// are guaranteed to represent the same transformation for all arguments. bool operator==(const product_operator &other) const; + // unary operators + + product_operator operator-() const; + product_operator operator+() const; + // right-hand arithmetics product_operator operator*(double other) const; @@ -372,6 +418,34 @@ class product_operator : public operator_sum { operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; + + // left-hand arithmetics + + // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. + template + friend product_operator operator*(double other, const product_operator &self); + template + friend operator_sum operator+(double other, const product_operator &self); + template + friend operator_sum operator-(double other, const product_operator &self); + template + friend product_operator operator*(std::complex other, const product_operator &self); + template + friend operator_sum operator+(std::complex other, const product_operator &self); + template + friend operator_sum operator-(std::complex other, const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + template + friend product_operator operator*(const T &other, const product_operator &self); + template + friend operator_sum operator+(const T &other, const product_operator &self); + template + friend operator_sum operator-(const T &other, const product_operator &self); }; diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 57bd633e40..519f4d3a30 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -107,7 +107,7 @@ void assert_product_equal(const cudaq::product_operator &expected_coefficient, const std::vector &expected_terms) { - auto sumterms_prod = ((cudaq::operator_sum)got).get_terms(); + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); From e4a07e2f0fc37b3b3752a0e18e4f7ab7798d2743 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 15:30:39 +0000 Subject: [PATCH 067/311] more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 187 ------------------- runtime/cudaq/dynamics/operator_sum.cpp | 115 +++++++++++- runtime/cudaq/dynamics/product_operators.cpp | 88 ++++++++- 3 files changed, 194 insertions(+), 196 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index c481c45d25..6010d478ea 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -19,21 +19,6 @@ namespace cudaq { // operator sum right-hand arithmetics -template -operator_sum operator_sum::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(double other) const { - return *this - scalar_operator(other); -} - template operator_sum& operator_sum::operator*=(double other) { *this *= scalar_operator(other); @@ -52,21 +37,6 @@ operator_sum& operator_sum::operator-=(double other) { return *this; } -template -operator_sum operator_sum::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - template operator_sum& operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); @@ -85,28 +55,6 @@ operator_sum& operator_sum::operator-=(std::complex -operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = std::move(this->get_terms()); - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const scalar_operator &other) const { - // FIXME: reserve length - auto combined_terms = std::move(this->get_terms()); - combined_terms.push_back(product_operator(other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const scalar_operator &other) const { - return *this + (other * (-1.0)); -} - template operator_sum& operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; @@ -125,29 +73,6 @@ operator_sum& operator_sum::operator-=(const scalar_operat return *this; } -template -operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = this->get_terms(); - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const HandlerTy &other) const { - auto combined_terms = std::move(this->get_terms()); - combined_terms.push_back(product_operator(1., other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = std::move(this->get_terms()); - combined_terms.push_back(product_operator(-1., other)); - return operator_sum(combined_terms); -} - template operator_sum& operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; @@ -256,88 +181,24 @@ operator_sum& operator_sum::operator+=(const operator_sum< // product operator right-hand arithmetics -template -product_operator product_operator::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(double other) const { - return *this - scalar_operator(other); -} - template product_operator& product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } -template -product_operator product_operator::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - template product_operator& product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } -template -product_operator product_operator::operator*(const scalar_operator &other) const { - return product_operator(this->coefficients[0] * other, this->terms[0]); -} - -template -operator_sum product_operator::operator+(const scalar_operator &other) const { - product_operator coefficient(other); - return operator_sum(coefficient, *this); -} - -template -operator_sum product_operator::operator-(const scalar_operator &other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, *this); -} - template product_operator& product_operator::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } -template -product_operator product_operator::operator*(const HandlerTy &other) const { - auto combined_terms = this->terms[0]; - combined_terms.push_back(other); - return product_operator(1., combined_terms); -} - -template -operator_sum product_operator::operator+(const HandlerTy &other) const { - return operator_sum(*this, product_operator(1., other)); -} - -template -operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum(*this, product_operator(-1., other)); -} - template product_operator& product_operator::operator*=(const HandlerTy &other) { *this = *this * other; @@ -394,12 +255,6 @@ operator_sum product_operator::operator-(const operator_su // instantiations -template -operator_sum operator_sum::operator*(double other) const; -template -operator_sum operator_sum::operator+(double other) const; -template -operator_sum operator_sum::operator-(double other) const; template operator_sum& operator_sum::operator*=(double other); template @@ -407,36 +262,18 @@ operator_sum& operator_sum::operator+= template operator_sum& operator_sum::operator-=(double other); template -operator_sum operator_sum::operator*(std::complex other) const; -template -operator_sum operator_sum::operator+(std::complex other) const; -template -operator_sum operator_sum::operator-(std::complex other) const; -template operator_sum& operator_sum::operator*=(std::complex other); template operator_sum& operator_sum::operator+=(std::complex other); template operator_sum& operator_sum::operator-=(std::complex other); template -operator_sum operator_sum::operator*(const scalar_operator &other) const; -template -operator_sum operator_sum::operator+(const scalar_operator &other) const; -template -operator_sum operator_sum::operator-(const scalar_operator &other) const; -template operator_sum& operator_sum::operator*=(const scalar_operator &other); template operator_sum& operator_sum::operator+=(const scalar_operator &other); template operator_sum& operator_sum::operator-=(const scalar_operator &other); template -operator_sum operator_sum::operator*(const elementary_operator &other) const; -template -operator_sum operator_sum::operator+(const elementary_operator &other) const; -template -operator_sum operator_sum::operator-(const elementary_operator &other) const; -template operator_sum& operator_sum::operator*=(const elementary_operator &other); template operator_sum& operator_sum::operator+=(const elementary_operator &other); @@ -467,37 +304,13 @@ operator_sum& operator_sum::operator-= template operator_sum& operator_sum::operator+=(const operator_sum &other); -template -product_operator product_operator::operator*(double other) const; -template -operator_sum product_operator::operator+(double other) const; -template -operator_sum product_operator::operator-(double other) const; template product_operator& product_operator::operator*=(double other); template -product_operator product_operator::operator*(std::complex other) const; -template -operator_sum product_operator::operator+(std::complex other) const; -template -operator_sum product_operator::operator-(std::complex other) const; -template product_operator& product_operator::operator*=(std::complex other); template -product_operator product_operator::operator*(const scalar_operator &other) const; -template -operator_sum product_operator::operator+(const scalar_operator &other) const; -template -operator_sum product_operator::operator-(const scalar_operator &other) const; -template product_operator& product_operator::operator*=(const scalar_operator &other); template -product_operator product_operator::operator*(const elementary_operator &other) const; -template -operator_sum product_operator::operator+(const elementary_operator &other) const; -template -operator_sum product_operator::operator-(const elementary_operator &other) const; -template product_operator& product_operator::operator*=(const elementary_operator &other); template product_operator product_operator::operator*(const product_operator &other) const; diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 23448cb5a8..2b4d3096e1 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -228,6 +228,119 @@ operator_sum operator_sum::operator-() template operator_sum operator_sum::operator+() const; +// right-hand arithmetics + +#define SUM_MULTIPLICATION(otherTy) \ + template \ + operator_sum operator_sum::operator*(otherTy other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size()); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(coeff * other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = this->terms; \ + return sum; \ + } + +SUM_MULTIPLICATION(double); +SUM_MULTIPLICATION(std::complex); +SUM_MULTIPLICATION(const scalar_operator &); + +#define SUM_ADDITION(otherTy, op) \ + template \ + operator_sum operator_sum::operator op(otherTy other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->terms.size() + 1); \ + coefficients.push_back(other); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + terms.push_back({}); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION(double, +); +SUM_ADDITION(double, -); +SUM_ADDITION(std::complex, +); +SUM_ADDITION(std::complex, -); +SUM_ADDITION(const scalar_operator &, +); +SUM_ADDITION(const scalar_operator &, -); + +template +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> terms; + terms.reserve(this->terms.size()); + for (auto term : this->terms) { + std::vector prod; + prod.reserve(term.size() + 1); + for (auto op : term) + prod.push_back(op); + prod.push_back(other); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = this->coefficients; + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_HANDLER(op) \ + template \ + operator_sum operator_sum::operator op( \ + const HandlerTy &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->terms.size() + 1); \ + coefficients.push_back(1.); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_HANDLER(+) +SUM_ADDITION_HANDLER(-) + +template +operator_sum operator_sum::operator*(double other) const; +template +operator_sum operator_sum::operator+(double other) const; +template +operator_sum operator_sum::operator-(double other) const; +template +operator_sum operator_sum::operator*(std::complex other) const; +template +operator_sum operator_sum::operator+(std::complex other) const; +template +operator_sum operator_sum::operator-(std::complex other) const; +template +operator_sum operator_sum::operator*(const scalar_operator &other) const; +template +operator_sum operator_sum::operator+(const scalar_operator &other) const; +template +operator_sum operator_sum::operator-(const scalar_operator &other) const; +template +operator_sum operator_sum::operator*(const elementary_operator &other) const; +template +operator_sum operator_sum::operator+(const elementary_operator &other) const; +template +operator_sum operator_sum::operator-(const elementary_operator &other) const; + // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ @@ -295,7 +408,7 @@ operator_sum operator*(const HandlerTy &other, const operator_sum \ - operator_sum operator op(const HandlerTy & other, \ + operator_sum operator op(const HandlerTy &other, \ const operator_sum &self) { \ std::vector coefficients; \ coefficients.reserve(self.terms.size() + 1); \ diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 5182409045..5be196a52e 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -219,6 +219,78 @@ product_operator product_operator::ope template product_operator product_operator::operator+() const; +// right-hand arithmetics + +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficients[0], this->terms[0]); \ + } + +PRODUCT_MULTIPLICATION(double); +PRODUCT_MULTIPLICATION(std::complex); +PRODUCT_MULTIPLICATION(const scalar_operator &); + +#define PRODUCT_ADDITION(otherTy, op) \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const { \ + return operator_sum(product_operator(other), op *this); \ + } + +PRODUCT_ADDITION(double, +); +PRODUCT_ADDITION(double, -); +PRODUCT_ADDITION(std::complex, +); +PRODUCT_ADDITION(std::complex, -); +PRODUCT_ADDITION(const scalar_operator &, +); +PRODUCT_ADDITION(const scalar_operator &, -); + +template +product_operator product_operator::operator*(const HandlerTy &other) const { + std::vector terms; + terms.reserve(this->terms[0].size() + 1); + for (auto term : this->terms[0]) + terms.push_back(term); + terms.push_back(other); + return product_operator(this->coefficients[0], std::move(terms)); +} + +#define PRODUCT_ADDITION_HANDLER(op) \ + template \ + operator_sum product_operator::operator op( \ + const HandlerTy &other) const { \ + return operator_sum(product_operator(1., other), op *this); \ + } + +PRODUCT_ADDITION_HANDLER(+) +PRODUCT_ADDITION_HANDLER(-) + +template +product_operator product_operator::operator*(double other) const; +template +operator_sum product_operator::operator+(double other) const; +template +operator_sum product_operator::operator-(double other) const; +template +product_operator product_operator::operator*(std::complex other) const; +template +operator_sum product_operator::operator+(std::complex other) const; +template +operator_sum product_operator::operator-(std::complex other) const; +template +product_operator product_operator::operator*(const scalar_operator &other) const; +template +operator_sum product_operator::operator+(const scalar_operator &other) const; +template +operator_sum product_operator::operator-(const scalar_operator &other) const; +template +product_operator product_operator::operator*(const elementary_operator &other) const; +template +operator_sum product_operator::operator+(const elementary_operator &other) const; +template +operator_sum product_operator::operator-(const elementary_operator &other) const; + // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ @@ -256,15 +328,15 @@ product_operator operator*(const HandlerTy &other, const product_oper return product_operator(self.coefficients[0], std::move(terms)); } -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(1., other), self); -} +#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy &other, \ + const product_operator &self) { \ + return operator_sum(product_operator(1., other), op self); \ + } -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(1., other), -self); -} +PRODUCT_ADDITION_HANDLER_REVERSE(+) +PRODUCT_ADDITION_HANDLER_REVERSE(-) template product_operator operator*(double other, const product_operator &self); From 9d48f3b0f12cfcde0c9bc2791da1d41665a38ca1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 16:22:04 +0000 Subject: [PATCH 068/311] more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 53 ------------- runtime/cudaq/dynamics/product_operators.cpp | 82 +++++++++++++++++++- runtime/cudaq/operators.h | 9 ++- 3 files changed, 89 insertions(+), 55 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index 6010d478ea..7bb1d6d3fe 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -205,22 +205,6 @@ product_operator& product_operator::operator*=(const Handl return *this; } -template -product_operator product_operator::operator*(const product_operator &other) const { - auto combined_terms = this->terms[0]; - combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); - return product_operator(1., combined_terms); -} - -template -operator_sum product_operator::operator+(const product_operator &other) const { - return operator_sum(*this, other); -} - -template -operator_sum product_operator::operator-(const product_operator &other) const { - return operator_sum(*this, other * (-1.)); -} template product_operator& product_operator::operator*=(const product_operator &other) { @@ -228,30 +212,6 @@ product_operator& product_operator::operator*=(const produ return *this; } -template -operator_sum product_operator::operator*(const operator_sum &other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) { - term = *this * term; - } - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator+(const operator_sum &other) const { - std::vector other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator-(const operator_sum &other) const { - auto negative_other = other * (-1.); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - // instantiations @@ -313,18 +273,5 @@ product_operator& product_operator::op template product_operator& product_operator::operator*=(const elementary_operator &other); template -product_operator product_operator::operator*(const product_operator &other) const; -template -operator_sum product_operator::operator+(const product_operator &other) const; -template -operator_sum product_operator::operator-(const product_operator &other) const; -template product_operator& product_operator::operator*=(const product_operator &other); -template -operator_sum product_operator::operator*(const operator_sum &other) const; -template -operator_sum product_operator::operator+(const operator_sum &other) const; -template -operator_sum product_operator::operator-(const operator_sum &other) const; - } \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 5be196a52e..d0139a4039 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -260,7 +260,7 @@ product_operator product_operator::operator*(const Handler template \ operator_sum product_operator::operator op( \ const HandlerTy &other) const { \ - return operator_sum(product_operator(1., other), op *this); \ + return operator_sum(product_operator(op 1., other), *this); \ } PRODUCT_ADDITION_HANDLER(+) @@ -291,6 +291,86 @@ operator_sum product_operator::operato template operator_sum product_operator::operator-(const elementary_operator &other) const; +template +product_operator product_operator::operator*(const product_operator &other) const { + std::vector terms; + terms.reserve(this->terms[0].size() + other.terms[0].size()); + for (auto term : this->terms[0]) + terms.push_back(term); + for (auto term : other.terms[0]) + terms.push_back(term); + return product_operator(this->coefficients[0] * other.coefficients[0], std::move(terms)); +} + +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(op other, *this); \ + } + +PRODUCT_ADDITION_PRODUCT(+) +PRODUCT_ADDITION_PRODUCT(-) + +template +operator_sum product_operator::operator*(const operator_sum &other) const { + std::vector coefficients; + coefficients.reserve(other.coefficients.size()); + for (auto coeff : other.coefficients) + coefficients.push_back(this->coefficients[0] * coeff); + std::vector> terms; + terms.reserve(other.terms.size()); + for (auto term : other.terms) { + std::vector prod; + prod.reserve(this->terms[0].size() + term.size()); + for (auto op : this->terms[0]) + prod.push_back(op); + for (auto op : term) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = std::move(terms); + return sum; +} + +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(other.coefficients.size() + 1); \ + coefficients.push_back(this->coefficients[0]); \ + for (auto coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(other.terms.size() + 1); \ + terms.push_back(this->terms[0]); \ + for (auto term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +PRODUCT_ADDITION_SUM(+) +PRODUCT_ADDITION_SUM(-) + +template +product_operator product_operator::operator*(const product_operator &other) const; +template +operator_sum product_operator::operator+(const product_operator &other) const; +template +operator_sum product_operator::operator-(const product_operator &other) const; +template +operator_sum product_operator::operator*(const operator_sum &other) const; +template +operator_sum product_operator::operator+(const operator_sum &other) const; +template +operator_sum product_operator::operator-(const operator_sum &other) const; + // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 3f248bf407..f16c88270d 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -301,7 +301,14 @@ class operator_sum { template friend operator_sum operator+(const T &other, const operator_sum &self); template - friend operator_sum operator-(const T &other, const operator_sum &self); + friend operator_sum operator-(const T &other, const operator_sum &self); + + template + friend operator_sum product_operator::operator*(const operator_sum &other) const; + template + friend operator_sum product_operator::operator+(const operator_sum &other) const; + template + friend operator_sum product_operator::operator-(const operator_sum &other) const; }; From 2a7fd34c97eb1d985e8e4be86177ab0049b6516d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 16:59:22 +0000 Subject: [PATCH 069/311] more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 61 ------------- runtime/cudaq/dynamics/operator_sum.cpp | 111 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 61 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index 7bb1d6d3fe..00ec426254 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -93,29 +93,6 @@ operator_sum& operator_sum::operator-=(const HandlerTy &ot return *this; } -template -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = this->get_terms(); - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = this->get_terms(); - combined_terms.push_back(other); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const product_operator &other) const { - std::vector> combined_terms = this->get_terms(); - combined_terms.push_back(other * (-1.)); - return operator_sum(combined_terms); -} - template operator_sum& operator_sum::operator*=(const product_operator &other) { *this = *this * other; @@ -134,32 +111,6 @@ operator_sum& operator_sum::operator-=(const product_opera return *this; } -template -operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = this->get_terms(); - std::vector> product_terms; - auto other_terms = other.get_terms(); - for (auto &term : self_terms) { - for (auto &other_term : other_terms) { - product_terms.push_back(term * other_term); - } - } - return operator_sum(product_terms); -} - -template -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = std::move(this->get_terms()); - std::vector> other_terms = std::move(other.get_terms()); - combined_terms.insert(combined_terms.end(), std::make_move_iterator(other_terms.begin()), std::make_move_iterator(other_terms.end())); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (other * (-1)); -} - template operator_sum& operator_sum::operator*=(const operator_sum &other) { *this = *this * other; @@ -240,24 +191,12 @@ operator_sum& operator_sum::operator+= template operator_sum& operator_sum::operator-=(const elementary_operator &other); template -operator_sum operator_sum::operator*(const product_operator &other) const; -template -operator_sum operator_sum::operator+(const product_operator &other) const; -template -operator_sum operator_sum::operator-(const product_operator &other) const; -template operator_sum& operator_sum::operator*=(const product_operator &other); template operator_sum& operator_sum::operator+=(const product_operator &other); template operator_sum& operator_sum::operator-=(const product_operator &other); template -operator_sum operator_sum::operator*(const operator_sum &other) const; -template -operator_sum operator_sum::operator+(const operator_sum &other) const; -template -operator_sum operator_sum::operator-(const operator_sum &other) const; -template operator_sum& operator_sum::operator*=(const operator_sum &other); template operator_sum& operator_sum::operator-=(const operator_sum &other); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 2b4d3096e1..91f5e7187a 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -341,6 +341,117 @@ operator_sum operator_sum::operator+(c template operator_sum operator_sum::operator-(const elementary_operator &other) const; +template +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector coefficients; + coefficients.reserve(this->coefficients.size()); + for (auto coeff : this->coefficients) + coefficients.push_back(other.coefficients[0] * coeff); + std::vector> terms; + terms.reserve(this->terms.size()); + for (auto term : this->terms) { + std::vector prod; + prod.reserve(term.size() + other.terms[0].size()); + for (auto op : term) + prod.push_back(op); + for (auto op : other.terms[0]) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_PRODUCT(op) \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + coefficients.push_back(op other.coefficients[0]); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + terms.push_back(other.terms[0]); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_PRODUCT(+) +SUM_ADDITION_PRODUCT(-) + +template +operator_sum operator_sum::operator*(const operator_sum &other) const { + std::vector coefficients; + coefficients.reserve(this->coefficients.size() * other.coefficients.size()); + for (auto coeff1 : this->coefficients) { + for (auto coeff2 : other.coefficients) + coefficients.push_back(coeff1 * coeff2); + } + std::vector> terms; + terms.reserve(this->terms.size() * other.terms.size()); + for (auto term1 : this->terms) { + for (auto term2 : other.terms) { + std::vector prod; + prod.reserve(term1.size() + term2.size()); + for (auto op : term1) + prod.push_back(op); + for (auto op : term2) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + } + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_SUM(op) \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + for (auto coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + for (auto term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_SUM(+); +SUM_ADDITION_SUM(-); + +template +operator_sum operator_sum::operator*(const product_operator &other) const; +template +operator_sum operator_sum::operator+(const product_operator &other) const; +template +operator_sum operator_sum::operator-(const product_operator &other) const; +template +operator_sum operator_sum::operator*(const operator_sum &other) const; +template +operator_sum operator_sum::operator+(const operator_sum &other) const; +template +operator_sum operator_sum::operator-(const operator_sum &other) const; + // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ From bf691ca5f390f044a77898ff8bbf6eebb6961818 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 20:07:09 +0000 Subject: [PATCH 070/311] finished cleanup of arithmetics Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 1 - runtime/cudaq/dynamics/arithmetics.cpp | 216 ----------------- runtime/cudaq/dynamics/memory.cpp | 221 ------------------ runtime/cudaq/dynamics/operator_sum.cpp | 207 +++++++++++++--- runtime/cudaq/dynamics/product_operators.cpp | 56 ++++- .../dynamics/{arithmetics.h => templates.h} | 0 runtime/cudaq/operators.h | 44 ++-- 7 files changed, 239 insertions(+), 506 deletions(-) delete mode 100644 runtime/cudaq/dynamics/arithmetics.cpp delete mode 100644 runtime/cudaq/dynamics/memory.cpp rename runtime/cudaq/dynamics/{arithmetics.h => templates.h} (100%) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 45bedd7d65..adad5ac53b 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -22,7 +22,6 @@ set(CUDAQ_OPS_SRC elementary_operators.cpp product_operators.cpp operator_sum.cpp - arithmetics.cpp schedule.cpp ) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp deleted file mode 100644 index 00ec426254..0000000000 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/operators.h" -#include -#include -#include -#include -#include -#include -#include - -namespace cudaq { - -// operator sum right-hand arithmetics - -template -operator_sum& operator_sum::operator*=(double other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(double other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(double other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator*=(std::complex other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(std::complex other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(std::complex other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const scalar_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const scalar_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const HandlerTy &other) { - this->coefficients.push_back(1.); - this->terms.push_back({other}); - return *this; -} - -template -operator_sum& operator_sum::operator-=(const HandlerTy &other) { - this->coefficients.push_back(-1.); - this->terms.push_back({other}); - return *this; -} - -template -operator_sum& operator_sum::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const product_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const product_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator*=(const operator_sum &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const operator_sum &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const operator_sum &other) { - *this = *this + other; - return *this; -} - - -// product operator right-hand arithmetics - -template -product_operator& product_operator::operator*=(double other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator& product_operator::operator*=(std::complex other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator& product_operator::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -product_operator& product_operator::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - - -template -product_operator& product_operator::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - - -// instantiations - -template -operator_sum& operator_sum::operator*=(double other); -template -operator_sum& operator_sum::operator+=(double other); -template -operator_sum& operator_sum::operator-=(double other); -template -operator_sum& operator_sum::operator*=(std::complex other); -template -operator_sum& operator_sum::operator+=(std::complex other); -template -operator_sum& operator_sum::operator-=(std::complex other); -template -operator_sum& operator_sum::operator*=(const scalar_operator &other); -template -operator_sum& operator_sum::operator+=(const scalar_operator &other); -template -operator_sum& operator_sum::operator-=(const scalar_operator &other); -template -operator_sum& operator_sum::operator*=(const elementary_operator &other); -template -operator_sum& operator_sum::operator+=(const elementary_operator &other); -template -operator_sum& operator_sum::operator-=(const elementary_operator &other); -template -operator_sum& operator_sum::operator*=(const product_operator &other); -template -operator_sum& operator_sum::operator+=(const product_operator &other); -template -operator_sum& operator_sum::operator-=(const product_operator &other); -template -operator_sum& operator_sum::operator*=(const operator_sum &other); -template -operator_sum& operator_sum::operator-=(const operator_sum &other); -template -operator_sum& operator_sum::operator+=(const operator_sum &other); - -template -product_operator& product_operator::operator*=(double other); -template -product_operator& product_operator::operator*=(std::complex other); -template -product_operator& product_operator::operator*=(const scalar_operator &other); -template -product_operator& product_operator::operator*=(const elementary_operator &other); -template -product_operator& product_operator::operator*=(const product_operator &other); -} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/memory.cpp b/runtime/cudaq/dynamics/memory.cpp deleted file mode 100644 index 36c713a114..0000000000 --- a/runtime/cudaq/dynamics/memory.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -// FILE TO BE DELETED - THERE ARE JUST SOME NOTES/EXPERIMENTS TO CHECK HOW TO AVOID UNNECESSARY COPIES - -#include -#include -#include -#include // enable_if, conjuction - -class Foo { -public: - int i; - Foo() = default; - Foo(int i) : i(i) {} - Foo(const Foo &other) : i(other.i) { - std::cout << "copy Foo" << std::endl; - } - - Foo(Foo &&other) { - *this = std::forward(other); - } - - Foo& operator=(const Foo& other) { - std::cout << "assignment Foo" << std::endl; - // Check for self-assignment - if (this != &other) { - i = other.i; - } - return *this; - } - - Foo& operator=(Foo&& other) noexcept { - std::cout << "move Foo" << std::endl; - this->i = other.i; - return *this; - } -}; - -class Baz { -public: - Foo f; - Baz(Foo&& foo) { - std::cout << "create Baz" << std::endl; - f = std::move(foo); - } - - Baz(const Foo &foo) : f(foo) { - std::cout << "create Baz" << std::endl; - } - - Baz(const Baz& other) : f(other.f) { - std::cout << "copy Baz" << std::endl; - } -}; - -class Bar { -private: - - void aggregate(const Baz& head) { - std::cout << "got last " << head.f.i << std::endl; - items.push_back(head.f); - } - - template - void aggregate(const Baz &head, Args&& ... args) - { - std::cout << "got " << head.f.i << std::endl; - items.push_back(head.f); - aggregate(std::forward(args)...); - } - -public: - std::vector items; - Bar() = default; - - Bar(Foo&& foo) { - std::cout << "create Bar from &&" << std::endl; - items.push_back(std::forward(foo)); - } - - Bar(Bar&& other) { - items = std::move(other.items); - } - - Bar(const Foo &foo) { - items.push_back(foo); - } - - Bar(const Bar &other) : items(other.items) { - std::cout << "copy Bar" << std::endl; - } - - //Bar(std::initializer_list args) : items(args) {} - - Bar& operator=(Bar&& other) noexcept { - std::cout << "move Bar" << std::endl; - this->items = std::forward>(other.items); - return *this; - } - - Bar& operator=(const Bar& other) { - std::cout << "assignment Bar" << std::endl; - // Check for self-assignment - if (this != &other) { - items = other.items; - } - return *this; - } - - template...>::value, void>> - Bar(const Args&... args) { - items.reserve(sizeof...(Args)); - std::cout << "create Bar from Baz" << std::endl; - aggregate(args...); - std::cout << "done" << std::endl; - } -}; - -class Dummy1 { -protected: - std::vector> terms; - Dummy1() = default; -public: - Dummy1(std::vector> data) : terms(data) {} - - std::vector get(int i) { - return terms[i]; - } -}; - -class Dummy2 : public Dummy1 { -public: - Dummy2(std::vector data) : Dummy1({data}) {} - - std::string get(int i) { - return terms[0][i]; - } -}; - -class Dummy3 { -public: - std::vector> items; - Dummy3(const std::vector& ops) { - std::cout << "construct Dummy3" << std::endl; - items.push_back(ops); - } - - Dummy3(std::vector&& ops) { - std::cout << "construct Dummy3" << std::endl; - items.push_back(std::move(ops)); - } -}; - -int main() -{ - Bar bar; - { - Foo foo(5); - Bar dummy(foo); // creates 1 copy of foo - std::cout << dummy.items[0].i << std::endl; - bar = Bar(foo); // creates 1 copy to construct bar, 1 copy when assigning - } - std::cout << bar.items[0].i << std::endl; - //std::cout << foo.i << std::endl; - - Baz op1(Foo(1)); - Baz op2(Foo(2)); - - Bar bar2(op1, op2); - std::cout << bar2.items[0].i << " " << bar2.items[1].i << std::endl; - - // FIXME: AVOID FOO COPY HERE - Bar bar3(Baz(Foo(3)), Baz(Foo(4))); - std::cout << bar3.items[0].i << " " << bar3.items[1].i << std::endl; - - //Bar(1, Dummy()); - - std::vector data1 = {"op1", "op2"}; - std::vector data2 = {"op3", "op4"}; - - Dummy1 d1({data1, data2}); - Dummy2 d2(data1); - - std::cout << d2.get(0) << " " << d2.get(1) << std::endl; - std::cout << ((Dummy1)d2).get(0)[0] << " " << ((Dummy1)d2).get(0)[1] << std::endl; - - std::cout << d1.get(0)[0] << " " << d1.get(0)[1] << std::endl; - std::cout << d1.get(1)[0] << " " << d1.get(1)[1] << std::endl; - - std::vector ops = {}; - ops.reserve(2); - { - ops.push_back(Bar(Foo(9))); - Bar op(Foo(10)); - ops.push_back(op); - } - - Dummy3 d3(std::move(ops)); - std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; - //std::cout << ops[0].items[0].i << std::endl; - - { - std::cout << std::endl; - // an initializer list will always make an extra copy (by design of the initializer list) - // d3 = Dummy3({Foo(11), Foo(12)}); -> this will make an extra copy... - std::vector ops2 = {}; - ops2.reserve(2); - ops2.push_back(Foo(11)); - ops2.push_back(Foo(12)); - d3 = Dummy3(std::move(ops2)); - } - std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; - - return 0; -} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 91f5e7187a..fa9f441e38 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -209,7 +209,7 @@ template operator_sum operator_sum::operator-() const { std::vector coefficients; coefficients.reserve(this->coefficients.size()); - for (auto coeff : this->coefficients) + for (auto &coeff : this->coefficients) coefficients.push_back(-1. * coeff); operator_sum sum; sum.coefficients = std::move(coefficients); @@ -235,7 +235,7 @@ operator_sum operator_sum::operator+() operator_sum operator_sum::operator*(otherTy other) const { \ std::vector coefficients; \ coefficients.reserve(this->coefficients.size()); \ - for (auto coeff : this->coefficients) \ + for (auto &coeff : this->coefficients) \ coefficients.push_back(coeff * other); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -251,14 +251,14 @@ SUM_MULTIPLICATION(const scalar_operator &); template \ operator_sum operator_sum::operator op(otherTy other) const { \ std::vector coefficients; \ - coefficients.reserve(this->terms.size() + 1); \ - coefficients.push_back(other); \ - for (auto coeff : this->coefficients) \ - coefficients.push_back(op coeff); \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op other); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ std::vector> terms; \ terms.reserve(this->terms.size() + 1); \ terms.push_back({}); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -277,10 +277,10 @@ template operator_sum operator_sum::operator*(const HandlerTy &other) const { std::vector> terms; terms.reserve(this->terms.size()); - for (auto term : this->terms) { + for (auto &term : this->terms) { std::vector prod; prod.reserve(term.size() + 1); - for (auto op : term) + for (auto &op : term) prod.push_back(op); prod.push_back(other); terms.push_back(std::move(prod)); @@ -296,16 +296,16 @@ operator_sum operator_sum::operator*(const HandlerTy &othe operator_sum operator_sum::operator op( \ const HandlerTy &other) const { \ std::vector coefficients; \ - coefficients.reserve(this->terms.size() + 1); \ - coefficients.push_back(1.); \ - for (auto coeff : this->coefficients) \ - coefficients.push_back(op coeff); \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op 1.); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ std::vector> terms; \ terms.reserve(this->terms.size() + 1); \ std::vector newTerm; \ newTerm.push_back(other); \ terms.push_back(std::move(newTerm)); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -345,16 +345,16 @@ template operator_sum operator_sum::operator*(const product_operator &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size()); - for (auto coeff : this->coefficients) + for (auto &coeff : this->coefficients) coefficients.push_back(other.coefficients[0] * coeff); std::vector> terms; terms.reserve(this->terms.size()); - for (auto term : this->terms) { + for (auto &term : this->terms) { std::vector prod; prod.reserve(term.size() + other.terms[0].size()); - for (auto op : term) + for (auto &op : term) prod.push_back(op); - for (auto op : other.terms[0]) + for (auto &op : other.terms[0]) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -370,12 +370,12 @@ operator_sum operator_sum::operator*(const product_operato const product_operator &other) const { \ std::vector coefficients; \ coefficients.reserve(this->coefficients.size() + 1); \ - for (auto coeff : this->coefficients) \ + for (auto &coeff : this->coefficients) \ coefficients.push_back(coeff); \ coefficients.push_back(op other.coefficients[0]); \ std::vector> terms; \ terms.reserve(this->terms.size() + 1); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ terms.push_back(other.terms[0]); \ operator_sum sum; \ @@ -391,19 +391,19 @@ template operator_sum operator_sum::operator*(const operator_sum &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size() * other.coefficients.size()); - for (auto coeff1 : this->coefficients) { - for (auto coeff2 : other.coefficients) + for (auto &coeff1 : this->coefficients) { + for (auto &coeff2 : other.coefficients) coefficients.push_back(coeff1 * coeff2); } std::vector> terms; terms.reserve(this->terms.size() * other.terms.size()); - for (auto term1 : this->terms) { - for (auto term2 : other.terms) { + for (auto &term1 : this->terms) { + for (auto &term2 : other.terms) { std::vector prod; prod.reserve(term1.size() + term2.size()); - for (auto op : term1) + for (auto &op : term1) prod.push_back(op); - for (auto op : term2) + for (auto &op : term2) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -420,15 +420,15 @@ operator_sum operator_sum::operator*(const operator_sum &other) const { \ std::vector coefficients; \ coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - for (auto coeff : this->coefficients) \ + for (auto &coeff : this->coefficients) \ coefficients.push_back(coeff); \ - for (auto coeff : other.coefficients) \ + for (auto &coeff : other.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ - for (auto term : other.terms) \ + for (auto &term : other.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -452,6 +452,139 @@ operator_sum operator_sum::operator+(c template operator_sum operator_sum::operator-(const operator_sum &other) const; +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum& operator_sum::operator*=(otherTy other) { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return *this; \ + } + +SUM_MULTIPLICATION_ASSIGNMENT(double); +SUM_MULTIPLICATION_ASSIGNMENT(std::complex); +SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); + +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum& operator_sum::operator op##=(otherTy other) { \ + this->coefficients.push_back(op other); \ + this->terms.push_back({}); \ + return *this; \ + } + +SUM_ADDITION_ASSIGNMENT(double, +); +SUM_ADDITION_ASSIGNMENT(double, -); +SUM_ADDITION_ASSIGNMENT(std::complex, +); +SUM_ADDITION_ASSIGNMENT(std::complex, -); +SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); +SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); + +template +operator_sum& operator_sum::operator*=(const HandlerTy &other) { + for (auto &term : this->terms) + term.push_back(other); + operator_sum sum; + return *this; +} + +#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const HandlerTy &other) { \ + coefficients.push_back(op 1.); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + this->terms.push_back(std::move(newTerm)); \ + return *this; \ + } + +SUM_ADDITION_HANDLER_ASSIGNMENT(+) +SUM_ADDITION_HANDLER_ASSIGNMENT(-) + +template +operator_sum& operator_sum::operator*=(const product_operator &other) { + for (auto &coeff : this->coefficients) + coeff *= other.coefficients[0]; + for (auto &term : this->terms) { + term.reserve(term.size() + other.terms[0].size()); + for (auto &op : other.terms[0]) + term.push_back(op); + } + return *this; +} + +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const product_operator &other) { \ + this->coefficients.push_back(op other.coefficients[0]); \ + this->terms.push_back(other.terms[0]); \ + return *this; \ + } + +SUM_ADDITION_PRODUCT_ASSIGNMENT(+) +SUM_ADDITION_PRODUCT_ASSIGNMENT(-) + +template +operator_sum& operator_sum::operator*=(const operator_sum &other) { + this->coefficients.reserve(this->coefficients.size() * other.coefficients.size()); + *this = *this * other; // we need to update all coefficients and terms anyway + return *this; +} + +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const operator_sum &other) { \ + this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + for (auto &coeff : other.coefficients) \ + this->coefficients.push_back(op coeff); \ + this->terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : other.terms) \ + this->terms.push_back(term); \ + return *this; \ + } + +SUM_ADDITION_SUM_ASSIGNMENT(+); +SUM_ADDITION_SUM_ASSIGNMENT(-); + +template +operator_sum& operator_sum::operator*=(double other); +template +operator_sum& operator_sum::operator+=(double other); +template +operator_sum& operator_sum::operator-=(double other); +template +operator_sum& operator_sum::operator*=(std::complex other); +template +operator_sum& operator_sum::operator+=(std::complex other); +template +operator_sum& operator_sum::operator-=(std::complex other); +template +operator_sum& operator_sum::operator*=(const scalar_operator &other); +template +operator_sum& operator_sum::operator+=(const scalar_operator &other); +template +operator_sum& operator_sum::operator-=(const scalar_operator &other); +template +operator_sum& operator_sum::operator*=(const elementary_operator &other); +template +operator_sum& operator_sum::operator+=(const elementary_operator &other); +template +operator_sum& operator_sum::operator-=(const elementary_operator &other); +template +operator_sum& operator_sum::operator*=(const product_operator &other); +template +operator_sum& operator_sum::operator+=(const product_operator &other); +template +operator_sum& operator_sum::operator-=(const product_operator &other); +template +operator_sum& operator_sum::operator*=(const operator_sum &other); +template +operator_sum& operator_sum::operator-=(const operator_sum &other); +template +operator_sum& operator_sum::operator+=(const operator_sum &other); + // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ @@ -460,7 +593,7 @@ operator_sum operator_sum::operator-(c const operator_sum &self) { \ std::vector coefficients; \ coefficients.reserve(self.coefficients.size()); \ - for (auto coeff : self.coefficients) \ + for (auto &coeff : self.coefficients) \ coefficients.push_back(coeff * other); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -479,12 +612,12 @@ SUM_MULTIPLICATION_REVERSE(const scalar_operator &); std::vector coefficients; \ coefficients.reserve(self.terms.size() + 1); \ coefficients.push_back(other); \ - for (auto coeff : self.coefficients) \ + for (auto &coeff : self.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(self.terms.size() + 1); \ terms.push_back({}); \ - for (auto term : self.terms) \ + for (auto &term : self.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -503,11 +636,11 @@ template operator_sum operator*(const HandlerTy &other, const operator_sum &self) { std::vector> terms; terms.reserve(self.terms.size()); - for (auto term : self.terms) { + for (auto &term : self.terms) { std::vector prod; prod.reserve(term.size() + 1); prod.push_back(other); - for (auto op : term) + for (auto &op : term) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -524,14 +657,14 @@ operator_sum operator*(const HandlerTy &other, const operator_sum coefficients; \ coefficients.reserve(self.terms.size() + 1); \ coefficients.push_back(1.); \ - for (auto coeff : self.coefficients) \ + for (auto &coeff : self.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(self.terms.size() + 1); \ std::vector newTerm; \ newTerm.push_back(other); \ terms.push_back(std::move(newTerm)); \ - for (auto term : self.terms) \ + for (auto &term : self.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index d0139a4039..44b52152d1 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -250,7 +250,7 @@ template product_operator product_operator::operator*(const HandlerTy &other) const { std::vector terms; terms.reserve(this->terms[0].size() + 1); - for (auto term : this->terms[0]) + for (auto &term : this->terms[0]) terms.push_back(term); terms.push_back(other); return product_operator(this->coefficients[0], std::move(terms)); @@ -295,9 +295,9 @@ template product_operator product_operator::operator*(const product_operator &other) const { std::vector terms; terms.reserve(this->terms[0].size() + other.terms[0].size()); - for (auto term : this->terms[0]) + for (auto &term : this->terms[0]) terms.push_back(term); - for (auto term : other.terms[0]) + for (auto &term : other.terms[0]) terms.push_back(term); return product_operator(this->coefficients[0] * other.coefficients[0], std::move(terms)); } @@ -316,16 +316,16 @@ template operator_sum product_operator::operator*(const operator_sum &other) const { std::vector coefficients; coefficients.reserve(other.coefficients.size()); - for (auto coeff : other.coefficients) + for (auto &coeff : other.coefficients) coefficients.push_back(this->coefficients[0] * coeff); std::vector> terms; terms.reserve(other.terms.size()); - for (auto term : other.terms) { + for (auto &term : other.terms) { std::vector prod; prod.reserve(this->terms[0].size() + term.size()); - for (auto op : this->terms[0]) + for (auto &op : this->terms[0]) prod.push_back(op); - for (auto op : term) + for (auto &op : term) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -342,12 +342,12 @@ operator_sum product_operator::operator*(const operator_su std::vector coefficients; \ coefficients.reserve(other.coefficients.size() + 1); \ coefficients.push_back(this->coefficients[0]); \ - for (auto coeff : other.coefficients) \ + for (auto &coeff : other.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(other.terms.size() + 1); \ terms.push_back(this->terms[0]); \ - for (auto term : other.terms) \ + for (auto &term : other.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -371,6 +371,42 @@ operator_sum product_operator::operato template operator_sum product_operator::operator-(const operator_sum &other) const; +#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + product_operator& product_operator::operator*=(otherTy other) { \ + this->coefficients[0] *= other; \ + return *this; \ + } + +PRODUCT_MULTIPLICATION_ASSIGNMENT(double); +PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); +PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); + +template +product_operator& product_operator::operator*=(const HandlerTy &other) { + this->terms[0].push_back(other); + return *this; +} + +template +product_operator& product_operator::operator*=(const product_operator &other) { + this->coefficients[0] *= other.coefficients[0]; + this->terms[0].reserve(this->terms[0].size() + other.terms[0].size()); + this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), other.terms[0].end()); + return *this; +} + +template +product_operator& product_operator::operator*=(double other); +template +product_operator& product_operator::operator*=(std::complex other); +template +product_operator& product_operator::operator*=(const scalar_operator &other); +template +product_operator& product_operator::operator*=(const elementary_operator &other); +template +product_operator& product_operator::operator*=(const product_operator &other); + // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ @@ -403,7 +439,7 @@ product_operator operator*(const HandlerTy &other, const product_oper std::vector terms; terms.reserve(self.terms[0].size() + 1); terms.push_back(other); - for (auto term : self.terms[0]) + for (auto &term : self.terms[0]) terms.push_back(term); return product_operator(self.coefficients[0], std::move(terms)); } diff --git a/runtime/cudaq/dynamics/arithmetics.h b/runtime/cudaq/dynamics/templates.h similarity index 100% rename from runtime/cudaq/dynamics/arithmetics.h rename to runtime/cudaq/dynamics/templates.h diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index f16c88270d..254df292f2 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,7 +8,7 @@ #pragma once -#include "dynamics/arithmetics.h" +#include "dynamics/templates.h" #include "definition.h" #include "utils/tensor.h" @@ -241,36 +241,37 @@ class operator_sum { operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - operator_sum& operator*=(double other); - operator_sum& operator+=(double other); - operator_sum& operator-=(double other); operator_sum operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - operator_sum& operator*=(std::complex other); - operator_sum& operator+=(std::complex other); - operator_sum& operator-=(std::complex other); operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - operator_sum& operator*=(const scalar_operator &other); - operator_sum& operator+=(const scalar_operator &other); - operator_sum& operator-=(const scalar_operator &other); operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum& operator*=(const HandlerTy &other); - operator_sum& operator+=(const HandlerTy &other); - operator_sum& operator-=(const HandlerTy &other); operator_sum operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - operator_sum& operator*=(const product_operator &other); - operator_sum& operator+=(const product_operator &other); - operator_sum& operator-=(const product_operator &other); operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; + + operator_sum& operator*=(double other); + operator_sum& operator+=(double other); + operator_sum& operator-=(double other); + operator_sum& operator*=(std::complex other); + operator_sum& operator+=(std::complex other); + operator_sum& operator-=(std::complex other); + operator_sum& operator*=(const scalar_operator &other); + operator_sum& operator+=(const scalar_operator &other); + operator_sum& operator-=(const scalar_operator &other); + operator_sum& operator*=(const HandlerTy &other); + operator_sum& operator+=(const HandlerTy &other); + operator_sum& operator-=(const HandlerTy &other); + operator_sum& operator*=(const product_operator &other); + operator_sum& operator+=(const product_operator &other); + operator_sum& operator-=(const product_operator &other); operator_sum& operator*=(const operator_sum &other); operator_sum& operator+=(const operator_sum &other); operator_sum& operator-=(const operator_sum &other); @@ -405,27 +406,28 @@ class product_operator : public operator_sum { product_operator operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - product_operator& operator*=(double other); product_operator operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - product_operator& operator*=(std::complex other); product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - product_operator& operator*=(const scalar_operator &other); product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator& operator*=(const HandlerTy &other); product_operator operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - product_operator& operator*=(const product_operator &other); operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; + product_operator& operator*=(double other); + product_operator& operator*=(std::complex other); + product_operator& operator*=(const scalar_operator &other); + product_operator& operator*=(const HandlerTy &other); + product_operator& operator*=(const product_operator &other); + // left-hand arithmetics // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. From 91d0e44998a4d9ea69d750353f529dd6efeca52f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 14:51:54 +0000 Subject: [PATCH 071/311] picking up Anthony's changes Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 2 + runtime/cudaq/dynamics/CMakeLists.txt | 2 + .../cudaq/dynamics/elementary_operators.cpp | 78 +- runtime/cudaq/dynamics/helpers.cpp | 225 ++--- runtime/cudaq/dynamics/manipulation.cpp | 107 +++ runtime/cudaq/dynamics/operator_sum.cpp | 136 ++- runtime/cudaq/dynamics/product_operators.cpp | 105 ++- runtime/cudaq/dynamics/scalar_operators.cpp | 1 - runtime/cudaq/operators.h | 110 ++- runtime/cudaq/utils/tensor.cpp | 54 +- runtime/cudaq/utils/tensor.h | 9 + .../dynamics/elementary_ops_arithmetic.cpp | 292 ++++--- unittests/dynamics/elementary_ops_simple.cpp | 91 +- unittests/dynamics/operator_sum.cpp | 795 +++++++++++++++-- .../dynamics/product_operators_arithmetic.cpp | 796 ++++++++++++++++-- unittests/dynamics/scalar_ops_arithmetic.cpp | 1 - unittests/dynamics/scalar_ops_simple.cpp | 1 - unittests/utils/Tensor.cpp | 29 + 18 files changed, 2354 insertions(+), 480 deletions(-) create mode 100644 runtime/cudaq/dynamics/manipulation.cpp diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index d2760841ec..444173d941 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -73,6 +73,8 @@ class CallbackFunction { return *this; } + bool operator!() { return (!_callback_func); } + matrix_2 operator()(std::map degrees, std::map> parameters) const { diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index adad5ac53b..a595c9bb2b 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -23,6 +23,8 @@ set(CUDAQ_OPS_SRC product_operators.cpp operator_sum.cpp schedule.cpp + manipulation.cpp + helpers.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 8e5f42b7e3..ad663b44d4 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" #include @@ -187,49 +186,64 @@ product_operator elementary_operator::parity(int degree) { return product_operator(1., op); } -product_operator -elementary_operator::displace(int degree, std::complex amplitude) { +product_operator elementary_operator::displace(int degree) { std::string op_id = "displace"; auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; - // if (op.m_ops.find(op_id) == op.m_ops.end()) { - // auto func = [&, degree](std::map dimensions, - // std::map> _none) { - // std::size_t dimension = dimensions[degree]; - // auto temp_mat = matrix_2(dimension, dimension); - // // // displace = exp[ (amplitude * create) - (conj(amplitude) * - // annihilate) ] - // // for (std::size_t i = 0; i + 1 < dimension; i++) { - // // temp_mat[{i + 1, i}] = - // // amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - // // temp_mat[{i, i + 1}] = - // // -1. * std::conj(amplitude) * std::sqrt(static_cast(i + - // 1)) + - // // 0.0 * 'j'; - // // } - // // Not ideal that our method of computing the matrix exponential - // // requires copies here. Maybe we can just use eigen directly here - // // to limit to one copy, but we can address that later. - // auto mat = temp_mat.exp(); - // return mat; - // }; - // op.define(op_id, op.expected_dimensions, func); - // } - throw std::runtime_error("currently have a bug in implementation."); + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&, degree](std::map dimensions, + std::map> parameters) { + std::size_t dimension = dimensions[degree]; + auto displacement_amplitude = parameters["displacement"]; + auto create = matrix_2(dimension, dimension); + auto annihilate = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + create[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + annihilate[{i, i + 1}] = + std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + auto term1 = displacement_amplitude * create; + auto term2 = std::conj(displacement_amplitude) * annihilate; + return (term1 - term2).exponential(); + }; + op.define(op_id, op.expected_dimensions, func); + } return product_operator(1., op); } -product_operator -elementary_operator::squeeze(int degree, std::complex amplitude) { - throw std::runtime_error("Not yet implemented."); + +product_operator elementary_operator::squeeze(int degree) { + std::string op_id = "squeeze"; + auto op = elementary_operator(op_id, {degree}); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&, degree](std::map dimensions, + std::map> parameters) { + std::size_t dimension = dimensions[degree]; + auto squeezing = parameters["squeezing"]; + auto create = matrix_2(dimension, dimension); + auto annihilate = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + create[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + annihilate[{i, i + 1}] = + std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + auto term1 = std::conj(squeezing) * annihilate.power(2); + auto term2 = squeezing * create.power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); + }; + op.define(op_id, op.expected_dimensions, func); + } + return product_operator(1., op); } -// evaluations matrix_2 elementary_operator::to_matrix( std::map dimensions, - std::map> parameters) { + std::map> parameters) const { return m_ops[id].generator(dimensions, parameters); } diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index be7906a30b..47c780a988 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -6,145 +6,148 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/operators.h" +#include #include "cudaq/helpers.h" #include "cudaq/cudm_error_handling.h" #include #include #include -namespace cudaq { -// Aggregate parameters from multiple mappings. -std::map OperatorHelpers::aggregate_parameters( - const std::vector> ¶meter_mappings) { - std::map parameter_descriptions; - - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; +class _OperatorHelpers { +public: + _OperatorHelpers() = default; + + // Aggregate parameters from multiple mappings. + static std::map aggregate_parameters( + const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; + } } } - } - - return parameter_descriptions; -} -// Extract documentation for a specific parameter from docstring. -std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, - const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; + return parameter_descriptions; } - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); - - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; - - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if (std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + // Extract documentation for a specific parameter from docstring. + static std::string parameter_docs(const std::string ¶m_name, + const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } + + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", + std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if (std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + } } + } catch (...) { + return ""; } - } catch (...) { + return ""; } - return ""; -} - -// Extract positional arguments and keyword-only arguments. -std::pair, std::map> -OperatorHelpers::args_from_kwargs( - const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); + // Extract positional arguments and keyword-only arguments. + static std::pair, std::map> + args_from_kwargs( + const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); + } } - } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); + } } - } - return {extracted_args, kwonly_dict}; -} - -// Generate all possible quantum states for given degrees and dimensions -std::vector -OperatorHelpers::generate_all_states(const std::vector °rees, - const std::map &dimensions) { - if (degrees.empty()) { - return {}; + return {extracted_args, kwonly_dict}; } - std::vector> states; - for (int state = 0; state < dimensions.at(degrees[0]); state++) { - states.push_back({std::to_string(state)}); - } + /// Generates all possible states for the given dimensions ordered according + /// to the sequence of degrees (ordering is relevant if dimensions differ). + static std::vector + generate_all_states(std::vector degrees, std::map dimensions) { + if (degrees.size() == 0) + return {}; + + std::vector states; + int range = dimensions[degrees[0]]; + for (auto state = 0; state < range; state++) { + states.push_back(std::to_string(state)); + } - for (size_t i = 1; i < degrees.size(); i++) { - std::vector> new_states; - for (const auto ¤t : states) { - for (int state = 0; state < dimensions.at(degrees[i]); state++) { - auto new_entry = current; - new_entry.push_back(std::to_string(state)); - new_states.push_back(new_entry); + for (auto degree = degrees.begin() + 1; degree != degrees.end(); ++degree) { + std::string term; + std::vector result; + for (auto current : states) { + for (auto state = 0; state < dimensions[degrees[*degree]]; state++) { + result.push_back(current + std::to_string(state)); + } } + states = result; } - states = new_states; - } - std::vector result; - for (const auto &state : states) { - std::ostringstream joined; - for (const auto &s : state) { - joined << s; - } - result.push_back(joined.str()); + return states; } - return result; -} - -// Permute a given eigen matrix -void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, - const std::vector &permutation) { - Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); - for (size_t i = 0; i < permutation.size(); i++) { - for (size_t j = 0; j < permutation.size(); j++) { - permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); + // Permutes the given matrix according to the given permutation. + // If states is the current order of vector entries on which the given matrix + // acts, and permuted_states is the desired order of an array on which the + // permuted matrix should act, then the permutation is defined such that + // [states[i] for i in permutation] produces permuted_states. + static cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation) { + auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); + std::vector> sorted_values; + for (std::size_t permuted : permutation) { + for (std::size_t permuted_again : permutation) { + sorted_values.push_back(matrix[{permuted, permuted_again}]); + } + } + int idx = 0; + for (std::size_t row = 0; row < result.get_rows(); row++) { + for (std::size_t col = 0; col < result.get_columns(); col++) { + result[{row, col}] = sorted_values[idx]; + idx++; + } } + return result; } - matrix = permuted_matrix; -} - -// Canonicalize degrees by sorting in descending order -std::vector -OperatorHelpers::canonicalize_degrees(const std::vector °rees) { - std::vector sorted_degrees = degrees; - std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); - return sorted_degrees; -} - -} // namespace cudaq + // Returns the degrees sorted in canonical order. + static std::vector canonicalize_degrees(std::vector degrees) { + std::sort(degrees.begin(), degrees.end(), std::greater()); + return degrees; + } +}; diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp new file mode 100644 index 0000000000..b4005eb804 --- /dev/null +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include "helpers.cpp" + +namespace cudaq { + +std::vector +MatrixArithmetics::_compute_permutation(std::vector op_degrees, + std::vector canon_degrees) { + auto states = + _OperatorHelpers::generate_all_states(canon_degrees, m_dimensions); + + std::vector reordering; + for (auto degree : op_degrees) + reordering.push_back(canon_degrees[degree]); + + std::vector result; + for (auto state : states) { + int index; + std::string term; + for (auto i : reordering) { + term += state[i]; + } + auto it = std::find(states.begin(), states.end(), term); + if (it != states.end()) + index = std::distance(states.begin(), it); + result.push_back(index); + } + + return result; +} + +// Given a matrix representation that acts on the given degrees or freedom, +// sorts the degrees and permutes the matrix to match that canonical order. +// Returns: +// A tuple consisting of the permuted matrix as well as the sequence of +// degrees of freedom in canonical order. +std::tuple> +MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, + std::vector op_degrees) { + auto canon_degrees = _OperatorHelpers::canonicalize_degrees(op_degrees); + if (op_degrees == canon_degrees) + return std::tuple>{op_matrix, canon_degrees}; + + auto permutation = this->_compute_permutation(op_degrees, canon_degrees); + auto result = _OperatorHelpers::permute_matrix(op_matrix, permutation); + return std::tuple>{result, canon_degrees}; +} + +EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, + EvaluatedMatrix op2) { + /// FIXME: do this check: + // assert len(frozenset(op1.degrees).intersection(op2.degrees)) == 0, \ + // "Operators should not have common degrees of freedom." + + auto op_degrees = op1.m_degrees; + std::copy(op2.m_degrees.begin(), op2.m_degrees.end(), + back_inserter(op_degrees)); + auto op_matrix = cudaq::kronecker(op1.m_matrix, op2.m_matrix); + auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); + return EvaluatedMatrix(new_degrees, new_matrix); +} + +EvaluatedMatrix MatrixArithmetics::mul(EvaluatedMatrix op1, + EvaluatedMatrix op2) { + // Elementary operators have sorted degrees such that we have a unique + // convention for how to define the matrix. Tensor products permute the + // computed matrix if necessary to guarantee that all operators always have + // sorted degrees. + if (op1.m_degrees != op2.m_degrees) + throw std::runtime_error( + "Operators should have the same order of degrees."); + return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix * op2.m_matrix)); +} + +EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, + EvaluatedMatrix op2) { + // Elementary operators have sorted degrees such that we have a unique + // convention for how to define the matrix. Tensor products permute the + // computed matrix if necessary to guarantee that all operators always have + // sorted degrees. + if (op1.m_degrees != op2.m_degrees) + throw std::runtime_error( + "Operators should have the same order of degrees."); + return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix + op2.m_matrix)); +} + +EvaluatedMatrix MatrixArithmetics::evaluate( + std::variant> op) { + // auto getDegrees = [](auto &&t) { return t.degrees; }; + // auto toMatrix = [&](auto &&t) { + // return t.to_matrix(this->m_dimensions, this->m_parameters); + // }; + // auto degrees = std::visit(getDegrees, op); + // auto matrix = std::visit(toMatrix, op); + // return EvaluatedMatrix(degrees, matrix); + throw std::runtime_error("implementation broken."); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index fa9f441e38..f6d63c6751 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" #include @@ -18,14 +17,124 @@ namespace cudaq { // private methods +template +cudaq::matrix_2 operator_sum::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const { + + auto terms = this->get_terms(); + + std::set degrees_set; + for (auto op : terms) { + for (auto degree : op.degrees()) { + degrees_set.insert(degree); + } + } + std::vector degrees(degrees_set.begin(), degrees_set.end()); + + // We need to make sure all matrices are of the same size to sum them up. + auto paddedTerm = [&](auto &&term) { + std::vector op_degrees; + for (auto op : term.get_terms()) { + for (auto degree : op.degrees) + op_degrees.push_back(degree); + } + for (auto degree : degrees) { + auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); + if (it == op_degrees.end()) { + term *= elementary_operator::identity(degree); + } + } + return term; + }; + + auto sum = EvaluatedMatrix(); + if (pad_terms) { + + sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, dimensions, + parameters, pad_terms)); + for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { + auto term = terms[term_idx]; + + auto eval = paddedTerm(term).m_evaluate(arithmetics, dimensions, + parameters, pad_terms); + sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + } + } else { + sum = + EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, dimensions, + parameters, pad_terms)); + for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { + auto term = terms[term_idx]; + auto eval = + term.m_evaluate(arithmetics, dimensions, parameters, pad_terms); + sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + } + } + return sum.matrix(); +} + template -std::vector> operator_sum::canonicalize_product(product_operator &prod) const { - throw std::runtime_error("not implemented"); +std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const { + std::vector scalars = {prod.get_coefficient()}; + auto non_scalars = prod.get_terms(); + + std::vector all_degrees; + for (auto op : non_scalars) { + for (auto degree : op.degrees) + all_degrees.push_back(degree); + } + + std::set unique_degrees(all_degrees.begin(), all_degrees.end()); + + if (all_degrees.size() == unique_degrees.size()) { + // Each operator acts on different degrees of freedom; they + // hence commute and can be reordered arbitrarily. + /// FIXME: Doing nothing for now + // std::sort(non_scalars.begin(), non_scalars.end(), [](auto op){ return + // op.degrees; }) + } else { + // Some degrees exist multiple times; order the scalars, identities, + // and zeros, but do not otherwise try to reorder terms. + std::vector zero_ops; + std::vector identity_ops; + std::vector non_commuting; + for (auto op : non_scalars) { + if (op.id == "zero") + zero_ops.push_back(op); + if (op.id == "identity") + identity_ops.push_back(op); + if (op.id != "zero" || op.id != "identity") + non_commuting.push_back(op); + } + + /// FIXME: Not doing the same sorting we do in python yet + std::vector sorted_non_scalars; + sorted_non_scalars.insert(sorted_non_scalars.end(), zero_ops.begin(), + zero_ops.end()); + sorted_non_scalars.insert(sorted_non_scalars.end(), identity_ops.begin(), + identity_ops.end()); + sorted_non_scalars.insert(sorted_non_scalars.end(), non_commuting.begin(), + non_commuting.end()); + non_scalars = sorted_non_scalars; + } + return std::make_tuple(scalars, non_scalars); } template -std::vector> operator_sum::_canonical_terms() const { - throw std::runtime_error("not implemented"); +std::tuple, std::vector> operator_sum::m_canonical_terms() const { + /// FIXME: Not doing the same sorting we do in python yet + std::tuple, std::vector> result; + std::vector scalars; + std::vector elementary_ops; + for (auto term : this->get_terms()) { + auto canon_term = m_canonicalize_product(term); + auto canon_scalars = std::get<0>(canon_term); + auto canon_elementary = std::get<1>(canon_term); + scalars.insert(scalars.end(), canon_scalars.begin(), canon_scalars.end()); + canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), canon_elementary.end()); + } + return std::make_tuple(scalars, elementary_ops); } template @@ -40,10 +149,15 @@ void operator_sum::aggregate_terms(const product_operator } template -std::vector> operator_sum::canonicalize_product(product_operator &prod) const; +cudaq::matrix_2 operator_sum::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const; template -std::vector> operator_sum::_canonical_terms() const; +std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; + +template +std::tuple, std::vector> operator_sum::m_canonical_terms() const; // no overload for a single product, since we don't want a constructor for a single term @@ -182,8 +296,10 @@ std::string operator_sum::to_string() const { template matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map ¶ms) const { - throw std::runtime_error("not implemented"); + const std::map> ¶meters) const { + /// FIXME: Not doing any conversion to spin op yet. + return m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, + parameters); } template @@ -191,7 +307,7 @@ std::string operator_sum::to_string() const; template matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map ¶ms) const; + const std::map> ¶ms) const; // comparisons diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 44b52152d1..2fe585324b 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" +#include "cudaq/dynamics/helpers.cpp" #include #include @@ -18,6 +18,99 @@ namespace cudaq { // private methods +cudaq::matrix_2 +_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::elementary_operator op, + std::vector degrees, std::map dimensions, + std::map> parameters) { + /// Creating the tensor product with op being last is most efficient. + std::vector padded; + for (const auto °ree : degrees) { + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == + op.degrees.end(), + degree) { + padded.push_back( + arithmetics.evaluate(cudaq::elementary_operator::identity(degree)) + .matrix()); + } + matrix_2 mat = op.to_matrix(dimensions, parameters); + padded.push_back(mat); + } + /// FIXME: This directly uses cudaq::kronecker instead of the tensor method. + /// I need to double check to make sure this gives the equivalent behavior + /// to the method used in python. + return cudaq::kronecker(padded.begin(), padded.end()); + ; +} + +template +cudaq::matrix_2 product_operator::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const { + /// Grab the underlying elementary operators. + auto terms = this->get_terms(); + + std::set noncanon_set; + for (const auto &op : terms) { + for (const auto °ree : op.degrees) { + noncanon_set.insert(degree); + } + } + std::vector noncanon_degrees(noncanon_set.begin(), noncanon_set.end()); + + // Calculate the total dimensions of the Hilbert space to create our + // identity matrix. + auto full_hilbert_size = 1; + for (const auto degree : noncanon_degrees) + full_hilbert_size *= dimensions[degree]; + cudaq::matrix_2 result(full_hilbert_size, full_hilbert_size); + // If this product operator consists only of scalar operator terms, + // we will avoid all of the below logic and just return the scalar value + // stored in an identity matrix spanning the full Hilbert space of the + // provided `dimensions`. + if (terms.size() > 0) { + if (pad_terms) { + // Sorting the degrees to avoid unnecessary permutations during the + // padding. + std::set noncanon_set; + for (const auto &op : terms) { + for (const auto °ree : op.degrees) { + noncanon_set.insert(degree); + } + } + auto degrees = _OperatorHelpers::canonicalize_degrees(noncanon_degrees); + auto evaluated = + EvaluatedMatrix(degrees, _padded_op(arithmetics, terms[0], + degrees, dimensions, parameters)); + + for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { + auto op = terms[op_idx]; + if (op.degrees.size() != 1) { + auto padded_op_to_print = + _padded_op(arithmetics, op, degrees, dimensions, parameters); + auto padded_mat = + EvaluatedMatrix(degrees, _padded_op(arithmetics, op, degrees, + dimensions, parameters)); + evaluated = arithmetics.mul(evaluated, padded_mat); + } + } + result = evaluated.matrix(); + } else { + auto evaluated = arithmetics.evaluate(terms[0]); + for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { + auto op = terms[op_idx]; + auto mat = op.to_matrix(dimensions, parameters); + evaluated = + arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + } + result = evaluated.matrix(); + } + } else { + result = cudaq::matrix_2::identity(full_hilbert_size); + } + auto coefficient = this->get_coefficient(); + return coefficient.evaluate(parameters) * result; +} + template void product_operator::aggregate_terms() {} @@ -65,6 +158,11 @@ scalar_operator product_operator::get_coefficient() const { return this->coefficients[0]; } +template +cudaq::matrix_2 product_operator::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const; + template std::vector product_operator::degrees() const; @@ -180,7 +278,8 @@ template matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const { if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) - throw std::runtime_error("not implemented"); + return this->m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, + parameters); return this->get_terms()[0].to_matrix(dimensions, parameters); } @@ -236,7 +335,7 @@ PRODUCT_MULTIPLICATION(const scalar_operator &); template \ operator_sum product_operator::operator op( \ otherTy other) const { \ - return operator_sum(product_operator(other), op *this); \ + return operator_sum(product_operator(op other), *this); \ } PRODUCT_ADDITION(double, +); diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 42fac4e7e6..b933ad0783 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" #include diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 254df292f2..0dc767e344 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -19,11 +19,11 @@ #include #include -// FIXME: DO WE REALLY NEED THE HANDLERTY OVERLOADS? -> EVERYTHING SHOULD BE A PRODUCT -// might be perf relevant to avoid the creation of unnecessary products... - namespace cudaq { +class MatrixArithmetics; + + class scalar_operator { private: @@ -148,11 +148,14 @@ class operator_sum { private: - std::vector> - canonicalize_product(product_operator &prod) const; + std::tuple, std::vector> + m_canonicalize_product(product_operator &prod) const; + + std::tuple, std::vector> + m_canonical_terms() const; - std::vector> - _canonical_terms() const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms = true) const; void aggregate_terms(); @@ -216,7 +219,7 @@ class operator_sum { /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. matrix_2 to_matrix(const std::map &dimensions = {}, - const std::map ¶ms = {}) const; + const std::map> ¶meters = {}) const; // comparisons @@ -319,14 +322,18 @@ class operator_sum { /// that can. template // handler needs to inherit from operation_handler class product_operator : public operator_sum { +friend class operator_sum; // FIXME: explicitly list members instead? private: void aggregate_terms(); - + template void aggregate_terms(const HandlerTy &head, Args&& ... args); + matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms = true) const; + public: // read-only properties @@ -524,9 +531,8 @@ class elementary_operator { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 - to_matrix(const std::map dimensions, - const std::map> parameters) const; + matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. @@ -543,11 +549,9 @@ class elementary_operator { static product_operator number(int degree); static product_operator parity(int degree); static product_operator position(int degree); - /// FIXME: amplitude should be a parameter that is only defined upon evaluation - static product_operator squeeze(int degree, - std::complex amplitude); - static product_operator displace(int degree, - std::complex amplitude); + /// Operators that accept parameters at runtime. + static product_operator squeeze(int degree); + static product_operator displace(int degree); /// @brief Adds the definition of an elementary operator with the given id to /// the class. After definition, an the defined elementary operator can be @@ -641,4 +645,76 @@ extern template class product_operator; extern template class operator_sum; #endif + +template +class OperatorArithmetics { +public: + /// @brief Accesses the relevant data to evaluate an operator expression + /// in the leaf nodes, that is in elementary and scalar operators. + TEval evaluate(product_operator &op); + + /// @brief Adds two operators that act on the same degrees of freedom. + TEval add(TEval val1, TEval val2); + + /// @brief Multiplies two operators that act on the same degrees of freedom. + TEval mul(TEval val1, TEval val2); + + /// @brief Computes the tensor product of two operators that act on different + /// degrees of freedom. + TEval tensor(TEval val1, TEval val2); +}; + +class EvaluatedMatrix { +friend class MatrixArithmetics; + +private: + std::vector m_degrees; + matrix_2 m_matrix; + +public: + EvaluatedMatrix() = default; + EvaluatedMatrix(std::vector degrees, matrix_2 matrix) + : m_degrees(degrees), m_matrix(matrix) {} + + /// @brief The degrees of freedom that the matrix of the evaluated value + /// applies to. + std::vector degrees() { return m_degrees; } + + /// @brief The matrix representation of an evaluated operator, according + /// to the sequence of degrees of freedom associated with the evaluated + /// value. + matrix_2 matrix() { return m_matrix; } +}; + +/// Encapsulates the functions needed to compute the matrix representation +/// of an operator expression. +class MatrixArithmetics : public OperatorArithmetics { +private: + std::map m_dimensions; + std::map> m_parameters; + std::vector _compute_permutation(std::vector op_degrees, + std::vector canon_degrees); + std::tuple> + _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); + +public: + MatrixArithmetics(std::map dimensions, + std::map> parameters) + : m_dimensions(dimensions), m_parameters(parameters) {} + + // Computes the tensor product of two evaluate operators that act on + // different degrees of freedom using the kronecker product. + EvaluatedMatrix tensor(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Multiplies two evaluated operators that act on the same degrees + // of freedom. + EvaluatedMatrix mul(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Adds two evaluated operators that act on the same degrees + // of freedom. + EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Computes the matrix of an ElementaryOperator or ScalarOperator using its + // `to_matrix` method. + EvaluatedMatrix + evaluate(std::variant> op); +}; + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index 6d4aaa9764..ce94678e57 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include "cudaq/utils/tensor.h" -#include +#include #include inline std::complex &access(std::complex *p, @@ -128,3 +128,55 @@ std::string cudaq::matrix_2::dump() const { out << '}'; return out.str(); } + +double _factorial(std::size_t value) { + if (value <= 1) + return 1; + return value * std::tgamma(value); +} + +// Calculate the power of a given matrix, `powers` times. +cudaq::matrix_2 cudaq::matrix_2::power(int powers) { + // Initialize as identity. + std::size_t rows = get_rows(); + std::size_t columns = get_columns(); + if (rows != columns) + throw std::runtime_error("Matrix power expects a square matrix."); + auto result = cudaq::matrix_2(rows, columns); + for (std::size_t i = 0; i < rows; i++) { + result[{i, i}] = 1.0 + 0.0j; + } + + // Calculate the matrix power iteratively. + for (std::size_t i = 0; i < powers; i++) { + result = result * *this; + } + return result; +} + +// Calculate the Taylor approximation to the exponential of the given matrix. +cudaq::matrix_2 cudaq::matrix_2::exponential() { + std::size_t rows = get_rows(); + std::size_t columns = get_columns(); + if (rows != columns) + throw std::runtime_error("Matrix exponential expects a square matrix."); + auto result = cudaq::matrix_2(rows, columns); + // Taylor Series Approximation, fixed at 20 steps. + std::size_t taylor_steps = 20; + for (std::size_t step = 0; step < taylor_steps; step++) { + auto term = this->power(step); + for (std::size_t i = 0; i < rows; i++) { + for (std::size_t j = 0; j < columns; j++) { + result[{i, j}] += term[{i, j}] / _factorial(step); + } + } + } + return result; +} + +cudaq::matrix_2 cudaq::matrix_2::identity(const std::size_t rows) { + auto result = cudaq::matrix_2(rows, rows); + for (std::size_t i = 0; i < rows; i++) + result[{i, i}] = 1. + 0.0j; + return result; +} diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 8287ab93de..18486385fa 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -99,6 +99,15 @@ class matrix_2 { friend matrix_2 kronecker(const matrix_2 &, const matrix_2 &); matrix_2 &kronecker_inplace(const matrix_2 &); + /// Matrix exponential, uses 20 terms of Taylor Series approximation. + matrix_2 exponential(); + + /// Matrix power. + matrix_2 power(int powers); + + /// Return a square identity matrix for the given size. + static matrix_2 identity(const std::size_t rows); + /// Kronecker a list of matrices. The list can be any container that has /// iterators defined. template diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 519f4d3a30..2fd90a57fe 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -9,12 +9,6 @@ #include "cudaq/operators.h" #include -/// STATUS: -/// 1. I've generated all of the `want` matrices for each test, and prepared -/// the test to check against the `got` matrix. Now waiting on finishing the -/// full `to_matrix` conversion to be able to do so. -/// - namespace utils_0 { void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_rank(), b.get_rank()); @@ -90,18 +84,21 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} void assert_product_equal(const cudaq::product_operator &got, const std::complex &expected_coefficient, @@ -115,6 +112,75 @@ void assert_product_equal(const cudaq::product_operator NOT TESTED ANYWHERE +/// We get an error in the test body when evaluating +/// the return of this function, since the `-1.0` value +/// is going out of scope somewhere down the line in its +/// conversion behind the scenes to a scalar operator. +cudaq::scalar_operator negate(cudaq::scalar_operator op) { + return -1.0 * op; +} + +TEST(OperatorExpressions, checkElementaryAgainstDouble) { + std::complex value = 0.125 + 0.125j; + + // `elementary_operator` + `complex` and `complex` + + // `elementary_operator` + { + auto elementary = cudaq::elementary_operator::annihilate(0); + + auto sum = value + elementary; + auto reverse = elementary + value; + + auto got_matrix = sum.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils_0::id_matrix(3); + auto want_matrix = scaled_identity + utils_0::annihilate_matrix(3); + auto want_matrix_reverse = utils_0::annihilate_matrix(3) + scaled_identity; + + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `elementary_operator` - `complex` and `complex` - `elementary_operator` + { + auto elementary = cudaq::elementary_operator::position(0); + + auto difference = value - elementary; + auto reverse = elementary - value; + + auto got_matrix = difference.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils_0::id_matrix(3); + auto want_matrix = scaled_identity - utils_0::position_matrix(3); + auto want_matrix_reverse = utils_0::position_matrix(3) - scaled_identity; + + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `elementary_operator` * `complex` and `complex` * + // `elementary_operator` + { + auto elementary = cudaq::elementary_operator::number(0); + + auto product = value * elementary; + auto reverse = elementary * value; + + auto got_matrix = product.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils_0::id_matrix(3); + auto want_matrix = scaled_identity * utils_0::number_matrix(3); + auto want_matrix_reverse = utils_0::number_matrix(3) * scaled_identity; + + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto function = [](std::map> parameters) { @@ -131,35 +197,30 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::scalar_operator(const_scale_factor); - // Produces an `operator_sum` type. auto sum = self + other; auto reverse = other + self; - // Check the `operator_sum` attributes. ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::annihilate_matrix(level_count); // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator + scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::parity(0); auto other = cudaq::scalar_operator(function); - // Produces an `operator_sum` type. auto sum = self + other; auto reverse = other + self; @@ -167,24 +228,22 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) + scaled_identity; + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + // {{"value", const_scale_factor}}); + // auto got_reverse_matrix = reverse.to_matrix( + // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = - scaled_identity + utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity + utils_0::parity_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator - scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::number(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `operator_sum` type. @@ -195,26 +254,27 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}, {}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) - scaled_identity; + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}); + // auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; auto want_reverse_matrix = - scaled_identity - utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity - utils_0::number_matrix(level_count); + // std::cout << "\nwant = \n" << want_matrix.dump() << "\n"; + // std::cout << "\ngot = \n" << got_matrix.dump() << "\n"; + // std::cout << "\nwant reverse = \n" << want_reverse_matrix.dump() << "\n"; + // std::cout << "\ngot reverse = \n" << got_reverse_matrix.dump() << "\n"; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator - scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::position(0); auto other = cudaq::scalar_operator(function); - // Produces an `operator_sum` type. auto sum = self - other; auto reverse = other - self; @@ -222,74 +282,76 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) + scaled_identity; + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + // {{"value", const_scale_factor}}); + // auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {{"value", + // const_scale_factor}}); auto want_matrix = + // utils_0::position_matrix(level_count) + scaled_identity; auto want_reverse_matrix = - scaled_identity + utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity + utils_0::position_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator * scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); + + std::vector want_degrees = {0}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = product.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}, {}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) * scaled_identity; + auto got_matrix = product.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = utils_0::momentum_matrix(level_count) * scaled_identity; auto want_reverse_matrix = - scaled_identity * utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity * utils_0::momentum_matrix(level_count); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator * scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::create(0); auto other = cudaq::scalar_operator(function); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("create", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("create", {0})}); + + std::vector want_degrees = {0}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); // auto got_matrix = product.to_matrix({{degree_index, level_count}}, - // {"value", const_scale_factor}); auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) * scaled_identity; + // {{"value", const_scale_factor}}); + // auto got_reverse_matrix = reverse.to_matrix( + // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = - scaled_identity * utils_0::annihilate_matrix(level_count); + scaled_identity * utils_0::create_matrix(level_count); // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } } @@ -309,12 +371,11 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Addition, different DOF's. @@ -322,21 +383,18 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(1); - // Produces an `operator_sum` type. auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + // auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full + create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, same DOF. @@ -344,17 +402,16 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(0); - // Produces an `operator_sum` type. auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + // auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) - utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, different DOF's. @@ -362,21 +419,19 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(1); - // Produces an `operator_sum` type. auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + // auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full - create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, same DOF. @@ -384,17 +439,18 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(0); - // Produces an `product_operator` type. auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. + std::vector want_degrees = {0}; + // ASSERT_TRUE(product.degrees() == want_degrees); - // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + // /// Check the matrices. + + // auto got_matrix = product.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) * utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, different DOF's. @@ -406,6 +462,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -426,6 +485,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// Keeping this fixed throughout. int level_count = 3; + std::complex value = 0.125 + 0.5j; /// `elementary_operator + operator_sum` and `operator_sum + /// elementary_operator` @@ -442,7 +502,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(reverse.n_terms() == 3); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); @@ -451,9 +510,9 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + // auto got_reverse_matrix = + // reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; // utils_0::checkEqual(want_matrix, got_matrix); @@ -498,7 +557,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { { auto self = cudaq::elementary_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + + auto operator_sum = cudaq::elementary_operator::squeeze(0) + cudaq::elementary_operator::identity(1); auto got = self * operator_sum; @@ -506,10 +565,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(got.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - for (auto &term : got.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - for (auto &term : reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); @@ -518,15 +575,16 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); + auto term_0_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::squeeze_matrix(level_count, value)); auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + // {{"squeezing", value}}); auto got_reverse_matrix = reverse.to_matrix({{0, + // level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; // utils_0::checkEqual(want_matrix, got_matrix); @@ -537,22 +595,23 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { { auto operator_sum = cudaq::elementary_operator::create(0) + cudaq::elementary_operator::identity(1); - operator_sum += cudaq::elementary_operator::annihilate(0); + operator_sum += cudaq::elementary_operator::displace(0); ASSERT_TRUE(operator_sum.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); + auto self_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::displace_matrix(level_count, value)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::create_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + // level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; // utils_0::checkEqual(want_matrix, got_matrix); } @@ -591,7 +650,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { operator_sum *= self; ASSERT_TRUE(operator_sum.n_terms() == 2); - for (auto &term : operator_sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp index 3b12805d3a..da8d3ace66 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -84,18 +84,21 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} } // namespace utils @@ -109,7 +112,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto id = cudaq::elementary_operator::identity(degree_index); - auto got_id = id.to_matrix({{degree_index, level_count}}, {}); + auto got_id = id.to_matrix({{degree_index, level_count}}); auto want_id = utils::id_matrix(level_count); utils::checkEqual(want_id, got_id); } @@ -119,7 +122,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto zero = cudaq::elementary_operator::zero(degree_index); - auto got_zero = zero.to_matrix({{degree_index, level_count}}, {}); + auto got_zero = zero.to_matrix({{degree_index, level_count}}); auto want_zero = utils::zero_matrix(level_count); utils::checkEqual(want_zero, got_zero); } @@ -129,8 +132,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto annihilate = cudaq::elementary_operator::annihilate(degree_index); - auto got_annihilate = - annihilate.to_matrix({{degree_index, level_count}}, {}); + auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); auto want_annihilate = utils::annihilate_matrix(level_count); utils::checkEqual(want_annihilate, got_annihilate); } @@ -140,7 +142,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto create = cudaq::elementary_operator::create(degree_index); - auto got_create = create.to_matrix({{degree_index, level_count}}, {}); + auto got_create = create.to_matrix({{degree_index, level_count}}); auto want_create = utils::create_matrix(level_count); utils::checkEqual(want_create, got_create); } @@ -150,7 +152,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto position = cudaq::elementary_operator::position(degree_index); - auto got_position = position.to_matrix({{degree_index, level_count}}, {}); + auto got_position = position.to_matrix({{degree_index, level_count}}); auto want_position = utils::position_matrix(level_count); utils::checkEqual(want_position, got_position); } @@ -160,7 +162,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto momentum = cudaq::elementary_operator::momentum(degree_index); - auto got_momentum = momentum.to_matrix({{degree_index, level_count}}, {}); + auto got_momentum = momentum.to_matrix({{degree_index, level_count}}); auto want_momentum = utils::momentum_matrix(level_count); utils::checkEqual(want_momentum, got_momentum); } @@ -170,7 +172,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto number = cudaq::elementary_operator::number(degree_index); - auto got_number = number.to_matrix({{degree_index, level_count}}, {}); + auto got_number = number.to_matrix({{degree_index, level_count}}); auto want_number = utils::number_matrix(level_count); utils::checkEqual(want_number, got_number); } @@ -180,28 +182,41 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto parity = cudaq::elementary_operator::parity(degree_index); - auto got_parity = parity.to_matrix({{degree_index, level_count}}, {}); + auto got_parity = parity.to_matrix({{degree_index, level_count}}); auto want_parity = utils::parity_matrix(level_count); utils::checkEqual(want_parity, got_parity); } } - // // // Displacement operator. - // // { - // // for (auto level_count : levels) { - // // auto amplitude = 1.0 + 1.0j; - // // auto displace = cudaq::elementary_operator::displace(degree_index, - // // amplitude); auto got_displace = - // utils::displace.to_matrix({{degree_index, - // // level_count}}, {}); auto want_displace = - // displace_matrix(level_count, - // // amplitude); utils::checkEqual(want_displace, got_displace); - // // } - // // } - - // TODO: Squeeze operator. + // Displacement operator. + { + for (auto level_count : levels) { + auto displacement = 2.0 + 1.0j; + auto displace = cudaq::elementary_operator::displace(degree_index); + auto got_displace = displace.to_matrix({{degree_index, level_count}}, + {{"displacement", displacement}}); + auto want_displace = utils::displace_matrix(level_count, displacement); + utils::checkEqual(want_displace, got_displace); + } + } + + // Squeeze operator. + { + for (auto level_count : levels) { + auto squeezing = 2.0 + 1.0j; + auto squeeze = cudaq::elementary_operator::squeeze(degree_index); + auto got_squeeze = squeeze.to_matrix({{degree_index, level_count}}, + {{"squeezing", squeezing}}); + auto want_squeeze = utils::squeeze_matrix(level_count, squeezing); + utils::checkEqual(want_squeeze, got_squeeze); + } + } } -// TEST(OperatorExpressions, checkCustomElementaryOps) { -// // pass -// } \ No newline at end of file +//TEST(OperatorExpressions, checkCustomElementaryOps) { + // pass + + // ex: + // operator acts upon {0,2} + // user gives us dimensions for {0,1,2} +//} diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 282ddd46d6..eedc8fbd94 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include @@ -85,18 +84,21 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} } // namespace utils_2 @@ -228,39 +230,84 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { + int level_count = 3; + std::complex value = 0.2 + 0.2j; // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto product = sum * cudaq::scalar_operator(0.1); - auto reverse = cudaq::scalar_operator(0.1) * sum; + auto product = sum * cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) * sum; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + /// Check the matrices. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = + // product.to_matrix({{1, level_count}, {2, level_count + 1}}); + // auto got_matrix_reverse = + // reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + //utils_2::checkEqual(want_matrix, got_matrix); + //utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` { + level_count = 2; auto original = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum = original + cudaq::scalar_operator(1.0); - auto reverse = cudaq::scalar_operator(1.0) + original; + auto sum = original + cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) + original; ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, + // {2,level_count+1}}); + + // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + // utils_2::create_matrix(level_count)); + // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + // utils_2::id_matrix(level_count)); + // auto sum_matrix = matrix0 + matrix1; + // auto scaled_identity = + // value * utils_2::id_matrix((level_count) * (level_count + 1)); + + // auto want_matrix = sum_matrix + scaled_identity; + // auto want_matrix_reverse = scaled_identity + sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -268,110 +315,288 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto original = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto difference = original - cudaq::scalar_operator(1.0); - auto reverse = cudaq::scalar_operator(1.0) - original; + auto difference = original - cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) - original; ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = difference.to_matrix({{1, level_count}, {2, + // level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, + // level_count}, {2, level_count+1}}); + + // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + // utils_2::create_matrix(level_count)); + // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + // utils_2::id_matrix(level_count)); + // auto sum_matrix = matrix0 + matrix1; + // auto scaled_identity = + // value * utils_2::id_matrix((level_count) * (level_count + 1)); + + // auto want_matrix = sum_matrix - scaled_identity; + // auto want_matrix_reverse = scaled_identity - sum_matrix; + // // utils_2::checkEqual(want_matrix, got_matrix); + // // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= scalar_operator` { auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::momentum(2); - sum *= cudaq::scalar_operator(0.1); + sum *= cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. + // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, + // {2, + // // level_count+1}}); + + // std::vector matrices_1 = { + // utils_2::id_matrix(level_count + 1), + // utils_2::create_matrix(level_count), + // utils_2::id_matrix(level_count)}; + // std::vector matrices_2 = { + // utils_2::momentum_matrix(level_count + 1), + // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; + // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + // auto scaled_identity = + // value * + // utils_2::id_matrix((level_count + 1) * level_count * level_count); + + // auto want_matrix = (matrix0 + matrix1) * scaled_identity; + // // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += scalar_operator` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::parity(1) + + cudaq::elementary_operator::position(2); - sum += cudaq::scalar_operator(1.0); + sum += cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. + // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, + // {2, + // // level_count+1}}); + + // std::vector matrices_1 = { + // utils_2::id_matrix(level_count + 1), + // utils_2::parity_matrix(level_count), + // utils_2::id_matrix(level_count)}; + // std::vector matrices_2 = { + // utils_2::position_matrix(level_count + 1), + // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; + // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + // auto scaled_identity = + // value * + // utils_2::id_matrix((level_count + 1) * level_count * level_count); + + // auto want_matrix = matrix0 + matrix1 + scaled_identity; + // // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= scalar_operator` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::number(1) + + cudaq::elementary_operator::annihilate(2); - sum -= cudaq::scalar_operator(1.0); + sum -= cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. + // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, + // {2, + // // level_count+1}}); + + // std::vector matrices_1 = { + // utils_2::id_matrix(level_count + 1), + // utils_2::number_matrix(level_count), + // utils_2::id_matrix(level_count)}; + // std::vector matrices_2 = { + // utils_2::annihilate_matrix(level_count + 1), + // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; + // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + // auto scaled_identity = + // value * + // utils_2::id_matrix((level_count + 1) * level_count * level_count); + + // auto want_matrix = (matrix0 + matrix1) - scaled_identity; + // // utils_2::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { - std::complex value = 0.1 + 0.1; + int level_count = 3; + std::complex value = 0.1 + 0.1j; + double double_value = 0.1; // `operator_sum * double` and `double * operator_sum` { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto product = sum * 2.0; - auto reverse = 2.0 * sum; + auto product = sum * double_value; + auto reverse = double_value * sum; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = product.to_matrix({{1, level_count}, {2, + // level_count+1}}, + // {}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + double` and `double + operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::elementary_operator::momentum(1) + + cudaq::elementary_operator::position(2); - auto sum = original + 2.0; - auto reverse = 2.0 + original; + auto sum = original + double_value; + auto reverse = double_value + original; ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::momentum_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::position_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix_reverse = scaled_identity + sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - double` and `double - operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::elementary_operator::parity(1) + + cudaq::elementary_operator::number(2); - auto difference = original - 2.0; - auto reverse = 2.0 - original; + auto difference = original - double_value; + auto reverse = double_value - original; ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::number_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= double` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::squeeze(1) + + cudaq::elementary_operator::squeeze(2); - sum *= 2.0; + sum *= double_value; ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - std::cout << "GOT: " << term.get_coefficient().evaluate() << std::endl; - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {{"squeezing", value}}); + + auto matrix0 = + cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::squeeze_matrix(level_count, value)); + auto matrix1 = + cudaq::kronecker(utils_2::squeeze_matrix(level_count + 1, value), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += double` @@ -379,9 +604,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - sum += 2.0; + sum += double_value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= double` @@ -389,9 +632,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - sum -= 2.0; + sum -= double_value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * std::complex` and `std::complex * @@ -415,6 +676,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = product.to_matrix({{1,level_count}, {2, + // level_count+1}}); auto got_matrix_reverse = + // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + std::complex` and `std::complex + @@ -428,6 +710,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix_reverse = scaled_identity + sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - std::complex` and `std::complex - @@ -441,12 +744,33 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = difference.to_matrix({{1,level_count}, {2, + // level_count+1}}); auto got_matrix_reverse = + // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= std::complex` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::displace(1) + + cudaq::elementary_operator::parity(2); sum *= value; @@ -455,84 +779,326 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {{"displacement", value}}); + + auto matrix0 = + cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::displace_matrix(level_count, value)); + auto matrix1 = cudaq::kronecker(utils_2::parity_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += std::complex` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::momentum(1) + + cudaq::elementary_operator::squeeze(2); sum += value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {{"squeezing", value}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::momentum_matrix(level_count)); + auto matrix1 = + cudaq::kronecker(utils_2::squeeze_matrix(level_count + 1, value), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= std::complex` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::position(1) + + cudaq::elementary_operator::number(2); sum -= value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::position_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::number_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { + int level_count = 2; + // `operator_sum + operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + cudaq::elementary_operator::create(3); auto sum = sum_0 + sum_1; ASSERT_TRUE(sum.n_terms() == 5); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, + // level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::create_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix + sum_1_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum - operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::position(2); + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::momentum(3); auto difference = sum_0 - sum_1; ASSERT_TRUE(difference.n_terms() == 5); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = difference.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::position_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::momentum_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix - sum_1_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::create(3); auto sum_product = sum_0 * sum_1; + auto sum_product_reverse = sum_1 * sum_0; ASSERT_TRUE(sum_product.n_terms() == 6); + ASSERT_TRUE(sum_product_reverse.n_terms() == 6); for (auto term : sum_product.get_terms()) ASSERT_TRUE(term.n_terms() == 2); + for (auto term : sum_product_reverse.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); auto + // got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::create_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix * sum_1_matrix; + auto want_matrix_reverse = sum_1_matrix * sum_0_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= operator_sum` { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::create(3); sum *= sum_1; ASSERT_TRUE(sum.n_terms() == 6); for (auto term : sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::create_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix * sum_1_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } } @@ -540,6 +1106,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { /// product operator test file. This mainly just tests the assignment operators /// between the two types. TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { + int level_count = 2; + // `operator_sum += product_operator` { auto product = cudaq::elementary_operator::annihilate(0) * @@ -550,6 +1118,38 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum += product; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, + // {2, level_count+2}}, {}); + std::vector matrices_0_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + std::vector matrices_1_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix + product_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= product_operator` @@ -562,6 +1162,38 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum -= product; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, + // {2, level_count+2}}); + std::vector matrices_0_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + std::vector matrices_1_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix - product_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum *= product_operator` @@ -574,9 +1206,40 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum *= product; ASSERT_TRUE(sum.n_terms() == 2); - for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 3); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, + // {2, level_count+2}}); + std::vector matrices_0_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + std::vector matrices_1_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix * product_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } -} +} \ No newline at end of file diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 9b399be7f1..2b8b3c04cb 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include @@ -87,22 +86,23 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} -} // namespace utils_1 +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} -/// TODO: Not yet testing the output matrices coming from this arithmetic. +} // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector levels = {2, 3, 4}; @@ -116,13 +116,11 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; - // auto got_matrix = got.to_matrix({{0, level_count}}, {}); - - // auto matrix0 = _annihilate_matrix(level_count); - // auto matrix1 = _create_matrix(level_count); - // auto want_matrix = matrix0 * matrix1; - - // ASSERT_TRUE(want_matrix == got_matrix); + auto got_matrix = got.to_matrix({{0, level_count}}); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); + auto want_matrix = matrix0 * matrix1; + //utils_1::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; ASSERT_TRUE(got.degrees() == want_degrees); @@ -136,46 +134,68 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::elementary_operator::create(1); cudaq::product_operator got = op0 * op1; - // auto got_matrix = - // got.to_matrix({{0, level_count}, {1, level_count}}, {}); - cudaq::product_operator got_reverse = op1 * op0; - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); - - // auto identity = _id_matrix(level_count); - // auto matrix0 = _annihilate_matrix(level_count); - // auto matrix1 = _create_matrix(level_count); - - // auto fullHilbert0 = identity.kronecker(matrix0); - // auto fullHilbert1 = matrix1.kronecker(identity); - // auto want_matrix = fullHilbert0 * fullHilbert1; - // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - // ASSERT_TRUE(want_matrix == got_matrix); - // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); std::vector want_degrees = {0, 1}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // got.to_matrix({{0, level_count}, {1, level_count}}, {}); + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); + + // auto identity = utils_1::id_matrix(level_count); + // auto matrix0 = utils_1::annihilate_matrix(level_count); + // auto matrix1 = utils_1::create_matrix(level_count); + + // auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + // auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + // auto want_matrix = fullHilbert0 * fullHilbert1; + // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } // Different degrees of freedom, non-consecutive. + // Should produce the same matrices as the above test. { for (auto level_count : levels) { auto op0 = cudaq::elementary_operator::annihilate(0); auto op1 = cudaq::elementary_operator::create(2); cudaq::product_operator got = op0 * op1; - // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, - // {}); - cudaq::product_operator got_reverse = op1 * op0; std::vector want_degrees = {0, 2}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, + // {}); + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0,level_count},{2,level_count}}, + // {}); + + auto identity = utils_1::id_matrix(level_count); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -187,14 +207,41 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::elementary_operator::create(2); cudaq::product_operator got = op0 * op1; - // auto got_matrix = - // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); - cudaq::product_operator got_reverse = op1 * op0; std::vector want_degrees = {0, 2}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, + // {}); + + auto identity = utils_1::id_matrix(level_count); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); + + /// Identity pad the operators to compute the kronecker + /// product to the full hilbert space. + std::vector matrices_0; + std::vector matrices_1; + matrices_0 = {identity, identity, matrix0}; + matrices_1 = {matrix1, identity, identity}; + + auto fullHilbert0 = + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(got_matrix, want_matrix); } } } @@ -246,6 +293,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::complex value_0 = 0.1 + 0.1; + int level_count = 3; /// `product_operator + complex` { @@ -261,6 +309,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + double` @@ -277,13 +347,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + scalar_operator` { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); + auto scalar_op = cudaq::scalar_operator(value_0); auto sum = scalar_op + product_op; auto reverse = product_op + scalar_op; @@ -294,6 +385,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - complex` @@ -310,6 +423,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - double` @@ -326,13 +461,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - scalar_operator` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); + auto product_op = cudaq::elementary_operator::momentum(0) * + cudaq::elementary_operator::momentum(1); + auto scalar_op = cudaq::scalar_operator(value_0); auto difference = scalar_op - product_op; auto reverse = product_op - scalar_op; @@ -343,13 +499,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::momentum_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * complex` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - + auto product_op = cudaq::elementary_operator::number(0) * + cudaq::elementary_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -364,13 +541,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::number_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * double` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - + auto product_op = cudaq::elementary_operator::parity(0) * + cudaq::elementary_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -385,17 +583,35 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse_reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::parity_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::parity_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * scalar_operator` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - - ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + auto product_op = cudaq::elementary_operator::position(0) * + cudaq::elementary_operator::position(1); + auto scalar_op = cudaq::scalar_operator(value_0); - auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; @@ -407,12 +623,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::position_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator *= complex` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product = cudaq::elementary_operator::number(0) * + cudaq::elementary_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.n_terms() == 2); @@ -420,12 +658,30 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= double` { auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + cudaq::elementary_operator::create(1); product *= 2.0; ASSERT_TRUE(product.n_terms() == 2); @@ -433,26 +689,64 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= scalar_operator` { auto product = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(0.1); + auto scalar_op = cudaq::scalar_operator(value_0); + product *= scalar_op; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); - ASSERT_TRUE(scalar_op.evaluate() == std::complex(0.1)); + ASSERT_TRUE(scalar_op.evaluate() == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { + int level_count = 3; + // `product_operator + product_operator` { auto term_0 = cudaq::elementary_operator::annihilate(0) * @@ -463,47 +757,199 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto sum = term_0 + term_1; ASSERT_TRUE(sum.n_terms() == 2); + + std::vector want_degrees = {0, 1, 2}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::annihilate_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix + term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` { auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + cudaq::elementary_operator::number(1); auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + cudaq::elementary_operator::momentum(2); auto difference = term_0 - term_1; ASSERT_TRUE(difference.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}, + // {2,level_count+1}}, {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::number_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::momentum_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix - term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * + auto term_0 = cudaq::elementary_operator::position(0) * cudaq::elementary_operator::annihilate(1); auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + cudaq::elementary_operator::parity(2); auto product = term_0 * term_1; ASSERT_TRUE(product.n_terms() == 4); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, + // {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::position_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::parity_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix * term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` { auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + cudaq::elementary_operator::number(1); auto term_1 = cudaq::elementary_operator::create(1) * cudaq::elementary_operator::annihilate(2); term_0 *= term_1; ASSERT_TRUE(term_0.n_terms() == 4); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, + // {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::number_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::annihilate_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix * term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { + int level_count = 3; + // `product_operator + elementary_operator` { auto product = cudaq::elementary_operator::annihilate(0) * @@ -515,6 +961,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix + elementary_matrix; + auto want_matrix_reverse = elementary_matrix + product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - elementary_operator` @@ -528,6 +996,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix - elementary_matrix; + auto want_matrix_reverse = elementary_matrix - product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * elementary_operator` @@ -541,6 +1031,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix * elementary_matrix; + auto want_matrix_reverse = elementary_matrix * product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator *= elementary_operator` @@ -552,11 +1064,31 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { product *= elementary; ASSERT_TRUE(product.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix * elementary_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { + int level_count = 3; + // `product_operator + operator_sum` { auto product = cudaq::elementary_operator::annihilate(0) * @@ -569,20 +1101,92 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); + + // Cast every term to full Hilbert space. + std::vector matrices_0_0 = { + utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_1::create_matrix(level_count + 1), + utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix + sum_matrix; + auto want_matrix_reverse = sum_matrix + product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` { auto product = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto original_sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original_difference = cudaq::elementary_operator::create(1) - + cudaq::elementary_operator::create(2); - auto difference = product - original_sum; - auto reverse = original_sum - product; + auto difference = product - original_difference; + auto reverse = original_difference - product; ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); + + // Cast every term to full Hilbert space. + std::vector matrices_0_0 = { + utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_1::create_matrix(level_count + 1), + utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto difference_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) - + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix - difference_matrix; + auto want_matrix_reverse = difference_matrix - product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -598,12 +1202,40 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 3); - } - - for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 3); - } + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); + + // Cast every term to full Hilbert space. + std::vector matrices_0_0 = { + utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_1::create_matrix(level_count + 1), + utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix * sum_matrix; + auto want_matrix_reverse = sum_matrix * product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index c3ef485228..b6d9a48518 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index 5ec80350f4..550b277f00 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include diff --git a/unittests/utils/Tensor.cpp b/unittests/utils/Tensor.cpp index feb7b05970..e996ee957a 100644 --- a/unittests/utils/Tensor.cpp +++ b/unittests/utils/Tensor.cpp @@ -162,3 +162,32 @@ TEST(Tensor, kroneckerOnList) { "{ { (3,3) (6,6) }\n { (4,4) (8,8) }\n { (5,5) (10,10) }\n }"); } } + +TEST(Tensor, exponential) { + { + cudaq::matrix_2 me({1., 1., 0.5, 0.0}, {2, 2}); + cudaq::matrix_2 mf({1., 0., 1., .5, .7, 0., 1., 0., 2.}, {3, 3}); + cudaq::matrix_2 mg( + {1., 0., .4, .6, .7, .8, .9, 0., .3, .1, .2, 1., 0., 0.5, 0.2, .5}, + {4, 4}); + + auto me_exp = me.exponential(); + auto mf_exp = mf.exponential(); + auto mg_exp = mg.exponential(); + + EXPECT_EQ( + me_exp.dump(), + "{ { (3.23795,0) (1.86268,0) }\n { (0.93134,0) (1.37527,0) }\n }"); + + EXPECT_EQ( + mf_exp.dump(), + "{ { (4.84921,0) (0,0) (5.4755,0) }\n { (1.46673,0) (2.01375,0) " + "(0.977708,0) }\n { (5.4755,0) (0,0) (10.3247,0) }\n }"); + + EXPECT_EQ(mg_exp.dump(), + "{ { (2.9751,0) (0.447969,0) (1.01977,0) (1.75551,0) }\n { " + "(2.10247,0) (2.55646,0) (1.97654,0) (1.39927,0) }\n { " + "(0.800451,0) (0.648569,0) (1.69099,0) (1.76597,0) }\n { " + "(0.498881,0) (1.05119,0) (0.753502,0) (2.03447,0) }\n }"); + } +} From 619b266b7cbd3b31e8e49ced5556d62eda5ffbec Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 15:08:26 +0000 Subject: [PATCH 072/311] just a quick renaming Signed-off-by: Bettina Heim --- docs/sphinx/api/languages/cpp_api.rst | 2 +- runtime/cudaq/dynamics/CMakeLists.txt | 2 +- runtime/cudaq/dynamics/manipulation.cpp | 2 +- ...ary_operators.cpp => matrix_operators.cpp} | 64 +++---- runtime/cudaq/dynamics/operator_sum.cpp | 166 +++++++++--------- runtime/cudaq/dynamics/product_operators.cpp | 136 +++++++------- runtime/cudaq/dynamics/templates.h | 50 +++--- runtime/cudaq/operators.h | 58 +++--- unittests/CMakeLists.txt | 4 +- ...ithmetic.cpp => matrix_ops_arithmetic.cpp} | 132 +++++++------- ...y_ops_simple.cpp => matrix_ops_simple.cpp} | 20 +-- unittests/dynamics/operator_sum.cpp | 156 ++++++++-------- .../dynamics/product_operators_arithmetic.cpp | 156 ++++++++-------- 13 files changed, 474 insertions(+), 474 deletions(-) rename runtime/cudaq/dynamics/{elementary_operators.cpp => matrix_operators.cpp} (81%) rename unittests/dynamics/{elementary_ops_arithmetic.cpp => matrix_ops_arithmetic.cpp} (85%) rename unittests/dynamics/{elementary_ops_simple.cpp => matrix_ops_simple.cpp} (90%) diff --git a/docs/sphinx/api/languages/cpp_api.rst b/docs/sphinx/api/languages/cpp_api.rst index b4c12fac2b..79065bebb2 100644 --- a/docs/sphinx/api/languages/cpp_api.rst +++ b/docs/sphinx/api/languages/cpp_api.rst @@ -242,7 +242,7 @@ Dynamics .. doxygenclass:: cudaq::scalar_operator :members: -.. doxygenclass:: cudaq::elementary_operator +.. doxygenclass:: cudaq::matrix_operator :members: .. doxygenclass:: cudaq::OperatorArithmetics diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index a595c9bb2b..170c7b963d 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -19,7 +19,7 @@ set(CUDAQ_OPS_SRC runge_kutta_integrator.cpp definition.cpp scalar_operators.cpp - elementary_operators.cpp + matrix_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index b4005eb804..8c245f818d 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -93,7 +93,7 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, } EvaluatedMatrix MatrixArithmetics::evaluate( - std::variant> op) { + std::variant> op) { // auto getDegrees = [](auto &&t) { return t.degrees; }; // auto toMatrix = [&](auto &&t) { // return t.to_matrix(this->m_dimensions, this->m_parameters); diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp similarity index 81% rename from runtime/cudaq/dynamics/elementary_operators.cpp rename to runtime/cudaq/dynamics/matrix_operators.cpp index ad663b44d4..4e7a7a6bcb 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -14,11 +14,11 @@ namespace cudaq { -std::map elementary_operator::m_ops = {}; +std::map matrix_operator::m_ops = {}; -product_operator elementary_operator::identity(int degree) { +product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -35,12 +35,12 @@ product_operator elementary_operator::identity(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::zero(int degree) { +product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -55,12 +55,12 @@ product_operator elementary_operator::zero(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::annihilate(int degree) { +product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -75,12 +75,12 @@ product_operator elementary_operator::annihilate(int degree }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::create(int degree) { +product_operator matrix_operator::create(int degree) { std::string op_id = "create"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -95,12 +95,12 @@ product_operator elementary_operator::create(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::position(int degree) { +product_operator matrix_operator::position(int degree) { std::string op_id = "position"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -119,12 +119,12 @@ product_operator elementary_operator::position(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::momentum(int degree) { +product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -143,12 +143,12 @@ product_operator elementary_operator::momentum(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::number(int degree) { +product_operator matrix_operator::number(int degree) { std::string op_id = "number"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -163,12 +163,12 @@ product_operator elementary_operator::number(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::parity(int degree) { +product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -183,12 +183,12 @@ product_operator elementary_operator::parity(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::displace(int degree) { +product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -209,13 +209,13 @@ product_operator elementary_operator::displace(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::squeeze(int degree) { +product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -237,11 +237,11 @@ product_operator elementary_operator::squeeze(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -matrix_2 elementary_operator::to_matrix( +matrix_2 matrix_operator::to_matrix( std::map dimensions, std::map> parameters) const { return m_ops[id].generator(dimensions, parameters); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index f6d63c6751..3debd0e6c0 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -42,7 +42,7 @@ cudaq::matrix_2 operator_sum::m_evaluate( for (auto degree : degrees) { auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); if (it == op_degrees.end()) { - term *= elementary_operator::identity(degree); + term *= matrix_operator::identity(degree); } } return term; @@ -96,9 +96,9 @@ std::tuple, std::vector> operator_sum zero_ops; - std::vector identity_ops; - std::vector non_commuting; + std::vector zero_ops; + std::vector identity_ops; + std::vector non_commuting; for (auto op : non_scalars) { if (op.id == "zero") zero_ops.push_back(op); @@ -109,7 +109,7 @@ std::tuple, std::vector> operator_sum sorted_non_scalars; + std::vector sorted_non_scalars; sorted_non_scalars.insert(sorted_non_scalars.end(), zero_ops.begin(), zero_ops.end()); sorted_non_scalars.insert(sorted_non_scalars.end(), identity_ops.begin(), @@ -124,9 +124,9 @@ std::tuple, std::vector> operator_sum std::tuple, std::vector> operator_sum::m_canonical_terms() const { /// FIXME: Not doing the same sorting we do in python yet - std::tuple, std::vector> result; + std::tuple, std::vector> result; std::vector scalars; - std::vector elementary_ops; + std::vector matrix_ops; for (auto term : this->get_terms()) { auto canon_term = m_canonicalize_product(term); auto canon_scalars = std::get<0>(canon_term); @@ -134,7 +134,7 @@ std::tuple, std::vector> operator_sum @@ -149,26 +149,26 @@ void operator_sum::aggregate_terms(const product_operator } template -cudaq::matrix_2 operator_sum::m_evaluate( +cudaq::matrix_2 operator_sum::m_evaluate( MatrixArithmetics arithmetics, std::map dimensions, std::map> parameters, bool pad_terms) const; template -std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; +std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; template -std::tuple, std::vector> operator_sum::m_canonical_terms() const; +std::tuple, std::vector> operator_sum::m_canonical_terms() const; // no overload for a single product, since we don't want a constructor for a single term template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2); +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2); template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); // read-only properties @@ -193,13 +193,13 @@ std::vector> operator_sum::get_terms() co } template -std::vector operator_sum::degrees() const; +std::vector operator_sum::degrees() const; template -int operator_sum::n_terms() const; +int operator_sum::n_terms() const; template -std::vector> operator_sum::get_terms() const; +std::vector> operator_sum::get_terms() const; // constructors @@ -241,25 +241,25 @@ operator_sum::operator_sum(operator_sum &&other) // no constructor for a single product, since that one should remain a product op template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2); +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2); template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); template -operator_sum::operator_sum(const std::vector> &terms); +operator_sum::operator_sum(const std::vector> &terms); template -operator_sum::operator_sum(std::vector> &&terms); +operator_sum::operator_sum(std::vector> &&terms); template -operator_sum::operator_sum(const operator_sum &other); +operator_sum::operator_sum(const operator_sum &other); template -operator_sum::operator_sum(operator_sum &&other); +operator_sum::operator_sum(operator_sum &&other); // assignments @@ -282,10 +282,10 @@ operator_sum& operator_sum::operator=(operator_sum& operator_sum::operator=(const operator_sum& other); +operator_sum& operator_sum::operator=(const operator_sum& other); template -operator_sum& operator_sum::operator=(operator_sum &&other); +operator_sum& operator_sum::operator=(operator_sum &&other); // evaluations @@ -303,10 +303,10 @@ matrix_2 operator_sum::to_matrix(const std::map &dimensions } template -std::string operator_sum::to_string() const; +std::string operator_sum::to_string() const; template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, +matrix_2 operator_sum::to_matrix(const std::map &dimensions, const std::map> ¶ms) const; // comparisons @@ -317,7 +317,7 @@ bool operator_sum::operator==(const operator_sum &other) c } template -bool operator_sum::operator==(const operator_sum &other) const; +bool operator_sum::operator==(const operator_sum &other) const; // unary operators @@ -339,10 +339,10 @@ operator_sum operator_sum::operator+() const { } template -operator_sum operator_sum::operator-() const; +operator_sum operator_sum::operator-() const; template -operator_sum operator_sum::operator+() const; +operator_sum operator_sum::operator+() const; // right-hand arithmetics @@ -433,29 +433,29 @@ SUM_ADDITION_HANDLER(+) SUM_ADDITION_HANDLER(-) template -operator_sum operator_sum::operator*(double other) const; +operator_sum operator_sum::operator*(double other) const; template -operator_sum operator_sum::operator+(double other) const; +operator_sum operator_sum::operator+(double other) const; template -operator_sum operator_sum::operator-(double other) const; +operator_sum operator_sum::operator-(double other) const; template -operator_sum operator_sum::operator*(std::complex other) const; +operator_sum operator_sum::operator*(std::complex other) const; template -operator_sum operator_sum::operator+(std::complex other) const; +operator_sum operator_sum::operator+(std::complex other) const; template -operator_sum operator_sum::operator-(std::complex other) const; +operator_sum operator_sum::operator-(std::complex other) const; template -operator_sum operator_sum::operator*(const scalar_operator &other) const; +operator_sum operator_sum::operator*(const scalar_operator &other) const; template -operator_sum operator_sum::operator+(const scalar_operator &other) const; +operator_sum operator_sum::operator+(const scalar_operator &other) const; template -operator_sum operator_sum::operator-(const scalar_operator &other) const; +operator_sum operator_sum::operator-(const scalar_operator &other) const; template -operator_sum operator_sum::operator*(const elementary_operator &other) const; +operator_sum operator_sum::operator*(const matrix_operator &other) const; template -operator_sum operator_sum::operator+(const elementary_operator &other) const; +operator_sum operator_sum::operator+(const matrix_operator &other) const; template -operator_sum operator_sum::operator-(const elementary_operator &other) const; +operator_sum operator_sum::operator-(const matrix_operator &other) const; template operator_sum operator_sum::operator*(const product_operator &other) const { @@ -556,17 +556,17 @@ SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); template -operator_sum operator_sum::operator*(const product_operator &other) const; +operator_sum operator_sum::operator*(const product_operator &other) const; template -operator_sum operator_sum::operator+(const product_operator &other) const; +operator_sum operator_sum::operator+(const product_operator &other) const; template -operator_sum operator_sum::operator-(const product_operator &other) const; +operator_sum operator_sum::operator-(const product_operator &other) const; template -operator_sum operator_sum::operator*(const operator_sum &other) const; +operator_sum operator_sum::operator*(const operator_sum &other) const; template -operator_sum operator_sum::operator+(const operator_sum &other) const; +operator_sum operator_sum::operator+(const operator_sum &other) const; template -operator_sum operator_sum::operator-(const operator_sum &other) const; +operator_sum operator_sum::operator-(const operator_sum &other) const; #define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -665,41 +665,41 @@ SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); template -operator_sum& operator_sum::operator*=(double other); +operator_sum& operator_sum::operator*=(double other); template -operator_sum& operator_sum::operator+=(double other); +operator_sum& operator_sum::operator+=(double other); template -operator_sum& operator_sum::operator-=(double other); +operator_sum& operator_sum::operator-=(double other); template -operator_sum& operator_sum::operator*=(std::complex other); +operator_sum& operator_sum::operator*=(std::complex other); template -operator_sum& operator_sum::operator+=(std::complex other); +operator_sum& operator_sum::operator+=(std::complex other); template -operator_sum& operator_sum::operator-=(std::complex other); +operator_sum& operator_sum::operator-=(std::complex other); template -operator_sum& operator_sum::operator*=(const scalar_operator &other); +operator_sum& operator_sum::operator*=(const scalar_operator &other); template -operator_sum& operator_sum::operator+=(const scalar_operator &other); +operator_sum& operator_sum::operator+=(const scalar_operator &other); template -operator_sum& operator_sum::operator-=(const scalar_operator &other); +operator_sum& operator_sum::operator-=(const scalar_operator &other); template -operator_sum& operator_sum::operator*=(const elementary_operator &other); +operator_sum& operator_sum::operator*=(const matrix_operator &other); template -operator_sum& operator_sum::operator+=(const elementary_operator &other); +operator_sum& operator_sum::operator+=(const matrix_operator &other); template -operator_sum& operator_sum::operator-=(const elementary_operator &other); +operator_sum& operator_sum::operator-=(const matrix_operator &other); template -operator_sum& operator_sum::operator*=(const product_operator &other); +operator_sum& operator_sum::operator*=(const product_operator &other); template -operator_sum& operator_sum::operator+=(const product_operator &other); +operator_sum& operator_sum::operator+=(const product_operator &other); template -operator_sum& operator_sum::operator-=(const product_operator &other); +operator_sum& operator_sum::operator-=(const product_operator &other); template -operator_sum& operator_sum::operator*=(const operator_sum &other); +operator_sum& operator_sum::operator*=(const operator_sum &other); template -operator_sum& operator_sum::operator-=(const operator_sum &other); +operator_sum& operator_sum::operator-=(const operator_sum &other); template -operator_sum& operator_sum::operator+=(const operator_sum &other); +operator_sum& operator_sum::operator+=(const operator_sum &other); // left-hand arithmetics @@ -792,28 +792,28 @@ SUM_ADDITION_HANDLER_REVERSE(+) SUM_ADDITION_HANDLER_REVERSE(-) template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); +operator_sum operator*(const scalar_operator &other, const operator_sum &self); template -operator_sum operator*(std::complex other, const operator_sum &self); +operator_sum operator*(std::complex other, const operator_sum &self); template -operator_sum operator*(double other, const operator_sum &self); +operator_sum operator*(double other, const operator_sum &self); template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); +operator_sum operator*(const matrix_operator &other, const operator_sum &self); template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); +operator_sum operator+(const scalar_operator &other, const operator_sum &self); template -operator_sum operator+(double other, const operator_sum &self); +operator_sum operator+(double other, const operator_sum &self); template -operator_sum operator+(std::complex other, const operator_sum &self); +operator_sum operator+(std::complex other, const operator_sum &self); template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); +operator_sum operator+(const matrix_operator &other, const operator_sum &self); template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); +operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -operator_sum operator-(double other, const operator_sum &self); +operator_sum operator-(double other, const operator_sum &self); template -operator_sum operator-(std::complex other, const operator_sum &self); +operator_sum operator-(std::complex other, const operator_sum &self); template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); +operator_sum operator-(const matrix_operator &other, const operator_sum &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 2fe585324b..87c8237f2e 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -19,7 +19,7 @@ namespace cudaq { // private methods cudaq::matrix_2 -_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::elementary_operator op, +_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::matrix_operator op, std::vector degrees, std::map dimensions, std::map> parameters) { /// Creating the tensor product with op being last is most efficient. @@ -29,7 +29,7 @@ _padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::elementary_operator op, op.degrees.end(), degree) { padded.push_back( - arithmetics.evaluate(cudaq::elementary_operator::identity(degree)) + arithmetics.evaluate(cudaq::matrix_operator::identity(degree)) .matrix()); } matrix_2 mat = op.to_matrix(dimensions, parameters); @@ -122,13 +122,13 @@ void product_operator::aggregate_terms(const HandlerTy &head, Args&& } template -void product_operator::aggregate_terms(const elementary_operator &item1, - const elementary_operator &item2); +void product_operator::aggregate_terms(const matrix_operator &item1, + const matrix_operator &item2); template -void product_operator::aggregate_terms(const elementary_operator &item1, - const elementary_operator &item2, - const elementary_operator &item3); +void product_operator::aggregate_terms(const matrix_operator &item1, + const matrix_operator &item2, + const matrix_operator &item3); // read-only properties @@ -159,21 +159,21 @@ scalar_operator product_operator::get_coefficient() const { } template -cudaq::matrix_2 product_operator::m_evaluate( +cudaq::matrix_2 product_operator::m_evaluate( MatrixArithmetics arithmetics, std::map dimensions, std::map> parameters, bool pad_terms) const; template -std::vector product_operator::degrees() const; +std::vector product_operator::degrees() const; template -int product_operator::n_terms() const; +int product_operator::n_terms() const; template -std::vector product_operator::get_terms() const; +std::vector product_operator::get_terms() const; template -scalar_operator product_operator::get_coefficient() const; +scalar_operator product_operator::get_coefficient() const; // constructors @@ -212,34 +212,34 @@ product_operator::product_operator(product_operator &&othe } template -product_operator::product_operator(scalar_operator coefficient); +product_operator::product_operator(scalar_operator coefficient); template -product_operator::product_operator(scalar_operator coefficient, - const elementary_operator &item1); +product_operator::product_operator(scalar_operator coefficient, + const matrix_operator &item1); template -product_operator::product_operator(scalar_operator coefficient, - const elementary_operator &item1, - const elementary_operator &item2); +product_operator::product_operator(scalar_operator coefficient, + const matrix_operator &item1, + const matrix_operator &item2); template -product_operator::product_operator(scalar_operator coefficient, - const elementary_operator &item1, - const elementary_operator &item2, - const elementary_operator &item3); +product_operator::product_operator(scalar_operator coefficient, + const matrix_operator &item1, + const matrix_operator &item2, + const matrix_operator &item3); template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); template -product_operator::product_operator(const product_operator &other); +product_operator::product_operator(const product_operator &other); template -product_operator::product_operator(product_operator &&other); +product_operator::product_operator(product_operator &&other); // assignments @@ -262,10 +262,10 @@ product_operator& product_operator::operator=(product_oper } template -product_operator& product_operator::operator=(const product_operator &other); +product_operator& product_operator::operator=(const product_operator &other); template -product_operator& product_operator::operator=(product_operator &&other); +product_operator& product_operator::operator=(product_operator &&other); // evaluations @@ -284,10 +284,10 @@ matrix_2 product_operator::to_matrix(std::map dimensions, } template -std::string product_operator::to_string() const; +std::string product_operator::to_string() const; template -matrix_2 product_operator::to_matrix(std::map dimensions, +matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const; // comparisons @@ -298,7 +298,7 @@ bool product_operator::operator==(const product_operator & } template -bool product_operator::operator==(const product_operator &other) const; +bool product_operator::operator==(const product_operator &other) const; // unary operators @@ -313,10 +313,10 @@ product_operator product_operator::operator+() const { } template -product_operator product_operator::operator-() const; +product_operator product_operator::operator-() const; template -product_operator product_operator::operator+() const; +product_operator product_operator::operator+() const; // right-hand arithmetics @@ -366,29 +366,29 @@ PRODUCT_ADDITION_HANDLER(+) PRODUCT_ADDITION_HANDLER(-) template -product_operator product_operator::operator*(double other) const; +product_operator product_operator::operator*(double other) const; template -operator_sum product_operator::operator+(double other) const; +operator_sum product_operator::operator+(double other) const; template -operator_sum product_operator::operator-(double other) const; +operator_sum product_operator::operator-(double other) const; template -product_operator product_operator::operator*(std::complex other) const; +product_operator product_operator::operator*(std::complex other) const; template -operator_sum product_operator::operator+(std::complex other) const; +operator_sum product_operator::operator+(std::complex other) const; template -operator_sum product_operator::operator-(std::complex other) const; +operator_sum product_operator::operator-(std::complex other) const; template -product_operator product_operator::operator*(const scalar_operator &other) const; +product_operator product_operator::operator*(const scalar_operator &other) const; template -operator_sum product_operator::operator+(const scalar_operator &other) const; +operator_sum product_operator::operator+(const scalar_operator &other) const; template -operator_sum product_operator::operator-(const scalar_operator &other) const; +operator_sum product_operator::operator-(const scalar_operator &other) const; template -product_operator product_operator::operator*(const elementary_operator &other) const; +product_operator product_operator::operator*(const matrix_operator &other) const; template -operator_sum product_operator::operator+(const elementary_operator &other) const; +operator_sum product_operator::operator+(const matrix_operator &other) const; template -operator_sum product_operator::operator-(const elementary_operator &other) const; +operator_sum product_operator::operator-(const matrix_operator &other) const; template product_operator product_operator::operator*(const product_operator &other) const { @@ -458,17 +458,17 @@ PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) template -product_operator product_operator::operator*(const product_operator &other) const; +product_operator product_operator::operator*(const product_operator &other) const; template -operator_sum product_operator::operator+(const product_operator &other) const; +operator_sum product_operator::operator+(const product_operator &other) const; template -operator_sum product_operator::operator-(const product_operator &other) const; +operator_sum product_operator::operator-(const product_operator &other) const; template -operator_sum product_operator::operator*(const operator_sum &other) const; +operator_sum product_operator::operator*(const operator_sum &other) const; template -operator_sum product_operator::operator+(const operator_sum &other) const; +operator_sum product_operator::operator+(const operator_sum &other) const; template -operator_sum product_operator::operator-(const operator_sum &other) const; +operator_sum product_operator::operator-(const operator_sum &other) const; #define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -496,15 +496,15 @@ product_operator& product_operator::operator*=(const produ } template -product_operator& product_operator::operator*=(double other); +product_operator& product_operator::operator*=(double other); template -product_operator& product_operator::operator*=(std::complex other); +product_operator& product_operator::operator*=(std::complex other); template -product_operator& product_operator::operator*=(const scalar_operator &other); +product_operator& product_operator::operator*=(const scalar_operator &other); template -product_operator& product_operator::operator*=(const elementary_operator &other); +product_operator& product_operator::operator*=(const matrix_operator &other); template -product_operator& product_operator::operator*=(const product_operator &other); +product_operator& product_operator::operator*=(const product_operator &other); // left-hand arithmetics @@ -554,28 +554,28 @@ PRODUCT_ADDITION_HANDLER_REVERSE(+) PRODUCT_ADDITION_HANDLER_REVERSE(-) template -product_operator operator*(double other, const product_operator &self); +product_operator operator*(double other, const product_operator &self); template -product_operator operator*(std::complex other, const product_operator &self); +product_operator operator*(std::complex other, const product_operator &self); template -product_operator operator*(const scalar_operator &other, const product_operator &self); +product_operator operator*(const scalar_operator &other, const product_operator &self); template -product_operator operator*(const elementary_operator &other, const product_operator &self); +product_operator operator*(const matrix_operator &other, const product_operator &self); template -operator_sum operator+(double other, const product_operator &self); +operator_sum operator+(double other, const product_operator &self); template -operator_sum operator+(std::complex other, const product_operator &self); +operator_sum operator+(std::complex other, const product_operator &self); template -operator_sum operator+(const scalar_operator &other, const product_operator &self); +operator_sum operator+(const scalar_operator &other, const product_operator &self); template -operator_sum operator+(const elementary_operator &other, const product_operator &self); +operator_sum operator+(const matrix_operator &other, const product_operator &self); template -operator_sum operator-(double other, const product_operator &self); +operator_sum operator-(double other, const product_operator &self); template -operator_sum operator-(std::complex other, const product_operator &self); +operator_sum operator-(std::complex other, const product_operator &self); template -operator_sum operator-(const scalar_operator &other, const product_operator &self); +operator_sum operator-(const scalar_operator &other, const product_operator &self); template -operator_sum operator-(const elementary_operator &other, const product_operator &self); +operator_sum operator-(const matrix_operator &other, const product_operator &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 8100562c9f..2a999beb8c 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -14,7 +14,7 @@ namespace cudaq { class scalar_operator; -class elementary_operator; +class matrix_operator; template class product_operator; @@ -75,54 +75,54 @@ operator_sum operator-(const HandlerTy &other, const operator_sum operator*(double other, const product_operator &self); +product_operator operator*(double other, const product_operator &self); extern template -operator_sum operator+(double other, const product_operator &self); +operator_sum operator+(double other, const product_operator &self); extern template -operator_sum operator-(double other, const product_operator &self); +operator_sum operator-(double other, const product_operator &self); extern template -product_operator operator*(std::complex other, const product_operator &self); +product_operator operator*(std::complex other, const product_operator &self); extern template -operator_sum operator+(std::complex other, const product_operator &self); +operator_sum operator+(std::complex other, const product_operator &self); extern template -operator_sum operator-(std::complex other, const product_operator &self); +operator_sum operator-(std::complex other, const product_operator &self); extern template -product_operator operator*(const scalar_operator &other, const product_operator &self); +product_operator operator*(const scalar_operator &other, const product_operator &self); extern template -operator_sum operator+(const scalar_operator &other, const product_operator &self); +operator_sum operator+(const scalar_operator &other, const product_operator &self); extern template -operator_sum operator-(const scalar_operator &other, const product_operator &self); +operator_sum operator-(const scalar_operator &other, const product_operator &self); extern template -product_operator operator*(const elementary_operator &other, const product_operator &self); +product_operator operator*(const matrix_operator &other, const product_operator &self); extern template -operator_sum operator+(const elementary_operator &other, const product_operator &self); +operator_sum operator+(const matrix_operator &other, const product_operator &self); extern template -operator_sum operator-(const elementary_operator &other, const product_operator &self); +operator_sum operator-(const matrix_operator &other, const product_operator &self); extern template -operator_sum operator*(double other, const operator_sum &self); +operator_sum operator*(double other, const operator_sum &self); extern template -operator_sum operator+(double other, const operator_sum &self); +operator_sum operator+(double other, const operator_sum &self); extern template -operator_sum operator-(double other, const operator_sum &self); +operator_sum operator-(double other, const operator_sum &self); extern template -operator_sum operator*(std::complex other, const operator_sum &self); +operator_sum operator*(std::complex other, const operator_sum &self); extern template -operator_sum operator+(std::complex other, const operator_sum &self); +operator_sum operator+(std::complex other, const operator_sum &self); extern template -operator_sum operator-(std::complex other, const operator_sum &self); +operator_sum operator-(std::complex other, const operator_sum &self); extern template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); +operator_sum operator*(const scalar_operator &other, const operator_sum &self); extern template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); +operator_sum operator+(const scalar_operator &other, const operator_sum &self); extern template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); +operator_sum operator-(const scalar_operator &other, const operator_sum &self); extern template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); +operator_sum operator*(const matrix_operator &other, const operator_sum &self); extern template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); +operator_sum operator+(const matrix_operator &other, const operator_sum &self); extern template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); +operator_sum operator-(const matrix_operator &other, const operator_sum &self); #endif } \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 0dc767e344..726d8f64a8 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -465,7 +465,7 @@ friend class operator_sum; // FIXME: explicitly list members instead? }; -class elementary_operator { +class matrix_operator { private: static std::map m_ops; @@ -485,23 +485,23 @@ class elementary_operator { /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - elementary_operator(std::string operator_id, const std::vector °rees) + matrix_operator(std::string operator_id, const std::vector °rees) : id(operator_id), degrees(degrees) {} // constructor - elementary_operator(std::string operator_id, std::vector &°rees) + matrix_operator(std::string operator_id, std::vector &°rees) : id(operator_id), degrees(std::move(degrees)) {} // copy constructor - elementary_operator(const elementary_operator &other) + matrix_operator(const matrix_operator &other) : degrees(other.degrees), id(other.id) {} // move constructor - elementary_operator(elementary_operator &&other) + matrix_operator(matrix_operator &&other) : degrees(std::move(other.degrees)), id(other.id) {} // assignment operator - elementary_operator& operator=(const elementary_operator& other) { + matrix_operator& operator=(const matrix_operator& other) { if (this != &other) { degrees = other.degrees; id = other.id; @@ -510,23 +510,23 @@ class elementary_operator { } // move assignment operator - elementary_operator& operator=(elementary_operator &&other) { + matrix_operator& operator=(matrix_operator &&other) { degrees = std::move(other.degrees); id = other.id; return *this; } - ~elementary_operator() = default; + ~matrix_operator() = default; /// @brief The degrees of freedom that the operator acts on in canonical /// order. std::vector degrees; std::string id; - /// @brief Return the `elementary_operator` as a string. + /// @brief Return the `matrix_operator` as a string. std::string to_string() const; - /// @brief Return the `elementary_operator` as a matrix. + /// @brief Return the `matrix_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -536,22 +536,22 @@ class elementary_operator { /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(const elementary_operator &other) const { + bool operator==(const matrix_operator &other) const { return this->id == other.id && this->degrees == other.degrees; } // Predefined operators. - static product_operator identity(int degree); - static product_operator zero(int degree); - static product_operator annihilate(int degree); - static product_operator create(int degree); - static product_operator momentum(int degree); - static product_operator number(int degree); - static product_operator parity(int degree); - static product_operator position(int degree); + static product_operator identity(int degree); + static product_operator zero(int degree); + static product_operator annihilate(int degree); + static product_operator create(int degree); + static product_operator momentum(int degree); + static product_operator number(int degree); + static product_operator parity(int degree); + static product_operator position(int degree); /// Operators that accept parameters at runtime. - static product_operator squeeze(int degree); - static product_operator displace(int degree); + static product_operator squeeze(int degree); + static product_operator displace(int degree); /// @brief Adds the definition of an elementary operator with the given id to /// the class. After definition, an the defined elementary operator can be @@ -582,13 +582,13 @@ class elementary_operator { template void define(std::string operator_id, std::map expected_dimensions, Func create) { - if (elementary_operator::m_ops.find(operator_id) != elementary_operator::m_ops.end()) { + if (matrix_operator::m_ops.find(operator_id) != matrix_operator::m_ops.end()) { // todo: make a nice error message to say op already exists throw; } auto defn = Definition(); defn.create_definition(operator_id, expected_dimensions, create); - elementary_operator::m_ops[operator_id] = defn; + matrix_operator::m_ops[operator_id] = defn; } }; @@ -638,11 +638,11 @@ class rydberg_hamiltonian : public operator_sum { std::optional>> delta_local; }; #ifdef CUDAQ_INSTANTIATE_TEMPLATES -template class product_operator; -template class operator_sum; +template class product_operator; +template class operator_sum; #else -extern template class product_operator; -extern template class operator_sum; +extern template class product_operator; +extern template class operator_sum; #endif @@ -651,7 +651,7 @@ class OperatorArithmetics { public: /// @brief Accesses the relevant data to evaluate an operator expression /// in the leaf nodes, that is in elementary and scalar operators. - TEval evaluate(product_operator &op); + TEval evaluate(product_operator &op); /// @brief Adds two operators that act on the same degrees of freedom. TEval add(TEval val1, TEval val2); @@ -714,7 +714,7 @@ class MatrixArithmetics : public OperatorArithmetics { // Computes the matrix of an ElementaryOperator or ScalarOperator using its // `to_matrix` method. EvaluatedMatrix - evaluate(std::variant> op); + evaluate(std::variant> op); }; } // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 77437e5d31..7b48ef7289 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -273,8 +273,8 @@ gtest_discover_tests(test_spin) # Create an executable for operators UnitTests set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/operator_sum.cpp - dynamics/elementary_ops_simple.cpp - dynamics/elementary_ops_arithmetic.cpp + dynamics/matrix_ops_simple.cpp + dynamics/matrix_ops_arithmetic.cpp dynamics/scalar_ops_simple.cpp dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp similarity index 85% rename from unittests/dynamics/elementary_ops_arithmetic.cpp rename to unittests/dynamics/matrix_ops_arithmetic.cpp index 2fd90a57fe..30bda31d22 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -100,11 +100,11 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } -void assert_product_equal(const cudaq::product_operator &got, +void assert_product_equal(const cudaq::product_operator &got, const std::complex &expected_coefficient, - const std::vector &expected_terms) { + const std::vector &expected_terms) { - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); @@ -124,10 +124,10 @@ cudaq::scalar_operator negate(cudaq::scalar_operator op) { TEST(OperatorExpressions, checkElementaryAgainstDouble) { std::complex value = 0.125 + 0.125j; - // `elementary_operator` + `complex` and `complex` + - // `elementary_operator` + // `matrix_operator` + `complex` and `complex` + + // `matrix_operator` { - auto elementary = cudaq::elementary_operator::annihilate(0); + auto elementary = cudaq::matrix_operator::annihilate(0); auto sum = value + elementary; auto reverse = elementary + value; @@ -143,9 +143,9 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `elementary_operator` - `complex` and `complex` - `elementary_operator` + // `matrix_operator` - `complex` and `complex` - `matrix_operator` { - auto elementary = cudaq::elementary_operator::position(0); + auto elementary = cudaq::matrix_operator::position(0); auto difference = value - elementary; auto reverse = elementary - value; @@ -161,10 +161,10 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `elementary_operator` * `complex` and `complex` * - // `elementary_operator` + // `matrix_operator` * `complex` and `complex` * + // `matrix_operator` { - auto elementary = cudaq::elementary_operator::number(0); + auto elementary = cudaq::matrix_operator::number(0); auto product = value * elementary; auto reverse = elementary * value; @@ -192,9 +192,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { int degree_index = 0; double const_scale_factor = 2.0; - // `elementary_operator + scalar_operator` + // `matrix_operator + scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); auto other = cudaq::scalar_operator(const_scale_factor); auto sum = self + other; @@ -216,9 +216,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator + scalar_operator` + // `matrix_operator + scalar_operator` { - auto self = cudaq::elementary_operator::parity(0); + auto self = cudaq::matrix_operator::parity(0); auto other = cudaq::scalar_operator(function); auto sum = self + other; @@ -241,9 +241,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator - scalar_operator` + // `matrix_operator - scalar_operator` { - auto self = cudaq::elementary_operator::number(0); + auto self = cudaq::matrix_operator::number(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `operator_sum` type. @@ -270,9 +270,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator - scalar_operator` + // `matrix_operator - scalar_operator` { - auto self = cudaq::elementary_operator::position(0); + auto self = cudaq::matrix_operator::position(0); auto other = cudaq::scalar_operator(function); auto sum = self - other; @@ -296,17 +296,17 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator * scalar_operator` + // `matrix_operator * scalar_operator` { - auto self = cudaq::elementary_operator::momentum(0); + auto self = cudaq::matrix_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); + utils_0::assert_product_equal(product, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); std::vector want_degrees = {0}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -324,17 +324,17 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator * scalar_operator` + // `matrix_operator * scalar_operator` { - auto self = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::create(0); auto other = cudaq::scalar_operator(function); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("create", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("create", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); std::vector want_degrees = {0}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -363,8 +363,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Addition, same DOF. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); // Produces an `operator_sum` type. auto sum = self + other; @@ -380,8 +380,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Addition, different DOF's. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(1); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); @@ -399,8 +399,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Subtraction, same DOF. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); @@ -416,8 +416,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Subtraction, different DOF's. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(1); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); @@ -436,8 +436,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Multiplication, same DOF. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); @@ -455,8 +455,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Multiplication, different DOF's. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(1); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); // Produces an `product_operator` type. auto product = self * other; @@ -487,13 +487,13 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { int level_count = 3; std::complex value = 0.125 + 0.5j; - /// `elementary_operator + operator_sum` and `operator_sum + - /// elementary_operator` + /// `matrix_operator + operator_sum` and `operator_sum + + /// matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -519,13 +519,13 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `elementary_operator - operator_sum` and `operator_sum - - /// elementary_operator` + /// `matrix_operator - operator_sum` and `operator_sum - + /// matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -552,13 +552,13 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `elementary_operator * operator_sum` and `operator_sum * - /// elementary_operator` + /// `matrix_operator * operator_sum` and `operator_sum * + /// matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::squeeze(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::squeeze(0) + + cudaq::matrix_operator::identity(1); auto got = self * operator_sum; auto reverse = operator_sum * self; @@ -591,11 +591,11 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `operator_sum += elementary_operator` + /// `operator_sum += matrix_operator` { - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); - operator_sum += cudaq::elementary_operator::displace(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + operator_sum += cudaq::matrix_operator::displace(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -616,11 +616,11 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_matrix, got_matrix); } - /// `operator_sum -= elementary_operator` + /// `operator_sum -= matrix_operator` { - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); - operator_sum -= cudaq::elementary_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + operator_sum -= cudaq::matrix_operator::annihilate(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -640,12 +640,12 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_matrix, got_matrix); } - /// `operator_sum *= elementary_operator` + /// `operator_sum *= matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); operator_sum *= self; diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp similarity index 90% rename from unittests/dynamics/elementary_ops_simple.cpp rename to unittests/dynamics/matrix_ops_simple.cpp index da8d3ace66..ac1caf4f10 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -111,7 +111,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Identity operator. { for (auto level_count : levels) { - auto id = cudaq::elementary_operator::identity(degree_index); + auto id = cudaq::matrix_operator::identity(degree_index); auto got_id = id.to_matrix({{degree_index, level_count}}); auto want_id = utils::id_matrix(level_count); utils::checkEqual(want_id, got_id); @@ -121,7 +121,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Zero operator. { for (auto level_count : levels) { - auto zero = cudaq::elementary_operator::zero(degree_index); + auto zero = cudaq::matrix_operator::zero(degree_index); auto got_zero = zero.to_matrix({{degree_index, level_count}}); auto want_zero = utils::zero_matrix(level_count); utils::checkEqual(want_zero, got_zero); @@ -131,7 +131,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Annihilation operator. { for (auto level_count : levels) { - auto annihilate = cudaq::elementary_operator::annihilate(degree_index); + auto annihilate = cudaq::matrix_operator::annihilate(degree_index); auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); auto want_annihilate = utils::annihilate_matrix(level_count); utils::checkEqual(want_annihilate, got_annihilate); @@ -141,7 +141,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Creation operator. { for (auto level_count : levels) { - auto create = cudaq::elementary_operator::create(degree_index); + auto create = cudaq::matrix_operator::create(degree_index); auto got_create = create.to_matrix({{degree_index, level_count}}); auto want_create = utils::create_matrix(level_count); utils::checkEqual(want_create, got_create); @@ -151,7 +151,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Position operator. { for (auto level_count : levels) { - auto position = cudaq::elementary_operator::position(degree_index); + auto position = cudaq::matrix_operator::position(degree_index); auto got_position = position.to_matrix({{degree_index, level_count}}); auto want_position = utils::position_matrix(level_count); utils::checkEqual(want_position, got_position); @@ -161,7 +161,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Momentum operator. { for (auto level_count : levels) { - auto momentum = cudaq::elementary_operator::momentum(degree_index); + auto momentum = cudaq::matrix_operator::momentum(degree_index); auto got_momentum = momentum.to_matrix({{degree_index, level_count}}); auto want_momentum = utils::momentum_matrix(level_count); utils::checkEqual(want_momentum, got_momentum); @@ -171,7 +171,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Number operator. { for (auto level_count : levels) { - auto number = cudaq::elementary_operator::number(degree_index); + auto number = cudaq::matrix_operator::number(degree_index); auto got_number = number.to_matrix({{degree_index, level_count}}); auto want_number = utils::number_matrix(level_count); utils::checkEqual(want_number, got_number); @@ -181,7 +181,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Parity operator. { for (auto level_count : levels) { - auto parity = cudaq::elementary_operator::parity(degree_index); + auto parity = cudaq::matrix_operator::parity(degree_index); auto got_parity = parity.to_matrix({{degree_index, level_count}}); auto want_parity = utils::parity_matrix(level_count); utils::checkEqual(want_parity, got_parity); @@ -192,7 +192,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto displacement = 2.0 + 1.0j; - auto displace = cudaq::elementary_operator::displace(degree_index); + auto displace = cudaq::matrix_operator::displace(degree_index); auto got_displace = displace.to_matrix({{degree_index, level_count}}, {{"displacement", displacement}}); auto want_displace = utils::displace_matrix(level_count, displacement); @@ -204,7 +204,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto squeezing = 2.0 + 1.0j; - auto squeeze = cudaq::elementary_operator::squeeze(degree_index); + auto squeeze = cudaq::matrix_operator::squeeze(degree_index); auto got_squeeze = squeeze.to_matrix({{degree_index, level_count}}, {{"squeezing", squeezing}}); auto want_squeeze = utils::squeeze_matrix(level_count, squeezing); diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index eedc8fbd94..5f6bb391b5 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -117,8 +117,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // Same degrees of freedom. // { // for (auto level_count : levels) { -// auto op0 = cudaq::elementary_operator::annihilate(0); -// auto op1 = cudaq::elementary_operator::create(0); +// auto op0 = cudaq::matrix_operator::annihilate(0); +// auto op1 = cudaq::matrix_operator::create(0); // cudaq::product_operator got = op0 * op1; // auto got_matrix = got.to_matrix({{0, level_count}}, {}); @@ -134,8 +134,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // // Different degrees of freedom. // // { // // for (auto level_count : levels) { -// // auto op0 = cudaq::elementary_operator::annihilate(0); -// // auto op1 = cudaq::elementary_operator::create(1); +// // auto op0 = cudaq::matrix_operator::annihilate(0); +// // auto op1 = cudaq::matrix_operator::create(1); // // cudaq::product_operator got = op0 * op1; // // auto got_matrix = @@ -163,8 +163,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // // Different degrees of freedom, non-consecutive. // // { // // for (auto level_count : levels) { -// // auto op0 = cudaq::elementary_operator::annihilate(0); -// // auto op1 = cudaq::elementary_operator::create(2); +// // auto op0 = cudaq::matrix_operator::annihilate(0); +// // auto op1 = cudaq::matrix_operator::create(2); // // // cudaq::product_operator got = op0 * op1; // // // auto got_matrix = @@ -177,8 +177,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // // provided. // // { // // for (auto level_count : levels) { -// // auto op0 = cudaq::elementary_operator::annihilate(0); -// // auto op1 = cudaq::elementary_operator::create(2); +// // auto op0 = cudaq::matrix_operator::annihilate(0); +// // auto op1 = cudaq::matrix_operator::create(2); // // // cudaq::product_operator got = op0 * op1; // // // auto got_matrix = @@ -209,7 +209,7 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // { // // Identity against constant. // { -// auto id_op = cudaq::elementary_operator::identity(0); +// auto id_op = cudaq::matrix_operator::identity(0); // auto scalar_op = cudaq::scalar_operator(value_0); // // auto multiplication = scalar_op * id_op; @@ -219,7 +219,7 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // Identity against constant from lambda. // { -// auto id_op = cudaq::elementary_operator::identity(0); +// auto id_op = cudaq::matrix_operator::identity(0); // auto scalar_op = cudaq::scalar_operator(function); // // auto multiplication = scalar_op * id_op; @@ -235,8 +235,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -279,8 +279,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` { level_count = 2; - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -312,8 +312,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -345,8 +345,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum *= scalar_operator` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::momentum(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::momentum(2); sum *= cudaq::scalar_operator(value); @@ -383,8 +383,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum += scalar_operator` { - auto sum = cudaq::elementary_operator::parity(1) + - cudaq::elementary_operator::position(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::position(2); sum += cudaq::scalar_operator(value); @@ -417,8 +417,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum -= scalar_operator` { - auto sum = cudaq::elementary_operator::number(1) + - cudaq::elementary_operator::annihilate(2); + auto sum = cudaq::matrix_operator::number(1) + + cudaq::matrix_operator::annihilate(2); sum -= cudaq::scalar_operator(value); @@ -457,8 +457,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` and `double * operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -501,8 +501,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + double` and `double + operator_sum` { - auto original = cudaq::elementary_operator::momentum(1) + - cudaq::elementary_operator::position(2); + auto original = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::position(2); auto sum = original + double_value; auto reverse = double_value + original; @@ -534,8 +534,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - double` and `double - operator_sum` { - auto original = cudaq::elementary_operator::parity(1) + - cudaq::elementary_operator::number(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::number(2); auto difference = original - double_value; auto reverse = double_value - original; @@ -567,8 +567,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= double` { - auto sum = cudaq::elementary_operator::squeeze(1) + - cudaq::elementary_operator::squeeze(2); + auto sum = cudaq::matrix_operator::squeeze(1) + + cudaq::matrix_operator::squeeze(2); sum *= double_value; @@ -601,8 +601,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum += double_value; @@ -629,8 +629,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum -= double_value; @@ -658,8 +658,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` and `std::complex * // operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = sum * value; auto reverse = value * sum; @@ -702,8 +702,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` and `std::complex + // operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = original + value; auto reverse = value + original; @@ -736,8 +736,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` and `std::complex - // operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto difference = original - value; auto reverse = value - original; @@ -769,8 +769,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= std::complex` { - auto sum = cudaq::elementary_operator::displace(1) + - cudaq::elementary_operator::parity(2); + auto sum = cudaq::matrix_operator::displace(1) + + cudaq::matrix_operator::parity(2); sum *= value; @@ -802,8 +802,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += std::complex` { - auto sum = cudaq::elementary_operator::momentum(1) + - cudaq::elementary_operator::squeeze(2); + auto sum = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::squeeze(2); sum += value; @@ -831,8 +831,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= std::complex` { - auto sum = cudaq::elementary_operator::position(1) + - cudaq::elementary_operator::number(2); + auto sum = cudaq::matrix_operator::position(1) + + cudaq::matrix_operator::number(2); sum -= value; @@ -863,11 +863,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { - auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(3); + auto sum_0 = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::create(3); auto sum = sum_0 + sum_1; @@ -920,11 +920,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum - operator_sum` { - auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::position(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::momentum(3); + auto sum_0 = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::position(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::momentum(3); auto difference = sum_0 - sum_1; @@ -977,11 +977,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum * operator_sum` { - auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(3); + auto sum_0 = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::create(3); auto sum_product = sum_0 * sum_1; auto sum_product_reverse = sum_1 * sum_0; @@ -1044,11 +1044,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum *= operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(3); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::create(3); sum *= sum_1; @@ -1110,10 +1110,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum += product_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum += product; @@ -1154,10 +1154,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum -= product_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum -= product; @@ -1198,10 +1198,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum *= product_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum *= product; diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 2b8b3c04cb..9c1747998d 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -111,8 +111,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Same degrees of freedom. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(0); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; @@ -130,8 +130,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Different degrees of freedom. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(1); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(1); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -166,8 +166,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Should produce the same matrices as the above test. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(2); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -203,8 +203,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // provided. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(2); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -265,7 +265,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { { // Annihilation against constant. { - auto id_op = cudaq::elementary_operator::annihilate(0); + auto id_op = cudaq::matrix_operator::annihilate(0); auto scalar_op = cudaq::scalar_operator(value_0); auto got = scalar_op * id_op; @@ -278,7 +278,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { // Annihilation against constant from lambda. { - auto id_op = cudaq::elementary_operator::annihilate(1); + auto id_op = cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(function); auto got = scalar_op * id_op; @@ -297,8 +297,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + complex` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto sum = value_0 + product_op; auto reverse = product_op + value_0; @@ -335,8 +335,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + double` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto sum = 2.0 + product_op; auto reverse = product_op + 2.0; @@ -372,8 +372,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + scalar_operator` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(value_0); auto sum = scalar_op + product_op; @@ -411,8 +411,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - complex` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto difference = value_0 - product_op; auto reverse = product_op - value_0; @@ -449,8 +449,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - double` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto difference = 2.0 - product_op; auto reverse = product_op - 2.0; @@ -486,8 +486,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - scalar_operator` { - auto product_op = cudaq::elementary_operator::momentum(0) * - cudaq::elementary_operator::momentum(1); + auto product_op = cudaq::matrix_operator::momentum(0) * + cudaq::matrix_operator::momentum(1); auto scalar_op = cudaq::scalar_operator(value_0); auto difference = scalar_op - product_op; @@ -525,8 +525,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * complex` { - auto product_op = cudaq::elementary_operator::number(0) * - cudaq::elementary_operator::number(1); + auto product_op = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -567,8 +567,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * double` { - auto product_op = cudaq::elementary_operator::parity(0) * - cudaq::elementary_operator::parity(1); + auto product_op = cudaq::matrix_operator::parity(0) * + cudaq::matrix_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -608,8 +608,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * scalar_operator` { - auto product_op = cudaq::elementary_operator::position(0) * - cudaq::elementary_operator::position(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto scalar_op = cudaq::scalar_operator(value_0); auto product = scalar_op * product_op; @@ -649,8 +649,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= complex` { - auto product = cudaq::elementary_operator::number(0) * - cudaq::elementary_operator::momentum(1); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.n_terms() == 2); @@ -680,8 +680,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= double` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::create(1); product *= 2.0; ASSERT_TRUE(product.n_terms() == 2); @@ -710,8 +710,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= scalar_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(value_0); product *= scalar_op; @@ -749,10 +749,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator + product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::annihilate(2); auto sum = term_0 + term_1; @@ -800,10 +800,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator - product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::number(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::momentum(2); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::number(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -849,10 +849,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator * product_operator` { - auto term_0 = cudaq::elementary_operator::position(0) * - cudaq::elementary_operator::annihilate(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::parity(2); + auto term_0 = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::annihilate(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -898,10 +898,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator *= product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::number(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::number(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::annihilate(2); term_0 *= term_1; @@ -950,11 +950,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { int level_count = 3; - // `product_operator + elementary_operator` + // `product_operator + matrix_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); auto sum = product + elementary; auto reverse = elementary + product; @@ -985,11 +985,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `product_operator - elementary_operator` + // `product_operator - matrix_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); auto difference = product - elementary; auto reverse = elementary - product; @@ -1020,11 +1020,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `product_operator * elementary_operator` + // `product_operator * matrix_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); auto product = term_0 * elementary; auto reverse = elementary * term_0; @@ -1055,11 +1055,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `product_operator *= elementary_operator` + // `product_operator *= matrix_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); product *= elementary; @@ -1091,10 +1091,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator + operator_sum` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto original_sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto original_sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = product + original_sum; auto reverse = original_sum + product; @@ -1141,10 +1141,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator - operator_sum` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto original_difference = cudaq::elementary_operator::create(1) - - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto original_difference = cudaq::matrix_operator::create(1) - + cudaq::matrix_operator::create(2); auto difference = product - original_difference; auto reverse = original_difference - product; @@ -1191,10 +1191,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator * operator_sum` { - auto original_product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original_product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = original_product * sum; auto reverse = sum * original_product; From d12f9cbb54021b429aa695dd1f6b9188c077896e Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 16:14:18 +0000 Subject: [PATCH 073/311] minor thing Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 9 +++- unittests/dynamics/matrix_ops_arithmetic.cpp | 12 ++--- .../dynamics/product_operators_arithmetic.cpp | 44 +++++++++---------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 3debd0e6c0..6a2973bb6c 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -174,7 +174,14 @@ void operator_sum::aggregate_terms(const product_operator std::vector operator_sum::degrees() const { - throw std::runtime_error("not implemented"); + std::set unsorted_degrees; + for (const std::vector &term : this->terms) { + for (const HandlerTy &op : term) + unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); + } + auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION + return degrees; } template diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 30bda31d22..e2cd52e337 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -309,8 +309,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); std::vector want_degrees = {0}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. @@ -337,8 +337,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); std::vector want_degrees = {0}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. @@ -443,7 +443,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { ASSERT_TRUE(product.n_terms() == 2); std::vector want_degrees = {0}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); // /// Check the matrices. @@ -463,7 +463,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { ASSERT_TRUE(product.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 9c1747998d..0718b27f1a 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -307,8 +307,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(sum.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -345,8 +345,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(sum.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -383,8 +383,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(sum.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -421,8 +421,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(difference.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(difference.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -459,8 +459,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(difference.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(difference.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -497,8 +497,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(difference.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(difference.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -539,8 +539,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -581,8 +581,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -621,8 +621,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -657,7 +657,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -688,7 +688,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -721,7 +721,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(scalar_op.evaluate() == value_0); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -759,7 +759,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 2); std::vector want_degrees = {0, 1, 2}; - // ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. From 5ce0c0778f25033e5dc46b2967300b8b66a3aa10 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 16:29:51 +0000 Subject: [PATCH 074/311] minor thing Signed-off-by: Bettina Heim --- unittests/dynamics/product_operators_arithmetic.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 0718b27f1a..5c2f76be57 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -102,6 +102,16 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } +void assert_product_equal(const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { + + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + ASSERT_TRUE(sumterms_prod.size() == 1); + ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); + ASSERT_TRUE(got.get_terms() == expected_terms); +} + } // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { @@ -115,6 +125,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; + utils_1::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {0}), cudaq::matrix_operator("create", {0})}); auto got_matrix = got.to_matrix({{0, level_count}}); auto matrix0 = utils_1::annihilate_matrix(level_count); From 0454209a1020434271ae095839ff244b39c71e56 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 31 Jan 2025 11:05:45 +0000 Subject: [PATCH 075/311] minor things Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/helpers.cpp | 24 ++++---- runtime/cudaq/dynamics/helpers.h | 32 ++++++++++ runtime/cudaq/dynamics/manipulation.cpp | 8 +-- runtime/cudaq/dynamics/operator_sum.cpp | 4 +- runtime/cudaq/dynamics/product_operators.cpp | 7 +-- unittests/dynamics/matrix_ops_arithmetic.cpp | 11 ++-- unittests/dynamics/matrix_ops_simple.cpp | 9 ++- unittests/dynamics/operator_sum.cpp | 9 ++- .../dynamics/product_operators_arithmetic.cpp | 58 +++++++++++++------ 9 files changed, 111 insertions(+), 51 deletions(-) create mode 100644 runtime/cudaq/dynamics/helpers.h diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 47c780a988..c5fcce1cbe 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -14,12 +14,15 @@ #include #include +namespace cudaq { +namespace detail { + class _OperatorHelpers { public: _OperatorHelpers() = default; // Aggregate parameters from multiple mappings. - static std::map aggregate_parameters( + std::map aggregate_parameters( const std::vector> ¶meter_mappings) { std::map parameter_descriptions; @@ -37,7 +40,7 @@ class _OperatorHelpers { } // Extract documentation for a specific parameter from docstring. - static std::string parameter_docs(const std::string ¶m_name, + std::string parameter_docs(const std::string ¶m_name, const std::string &docs) { if (param_name.empty() || docs.empty()) { return ""; @@ -69,7 +72,7 @@ class _OperatorHelpers { } // Extract positional arguments and keyword-only arguments. - static std::pair, std::map> + std::pair, std::map> args_from_kwargs( const std::map &kwargs, const std::vector &required_args, @@ -96,7 +99,7 @@ class _OperatorHelpers { /// Generates all possible states for the given dimensions ordered according /// to the sequence of degrees (ordering is relevant if dimensions differ). - static std::vector + std::vector generate_all_states(std::vector degrees, std::map dimensions) { if (degrees.size() == 0) return {}; @@ -121,12 +124,7 @@ class _OperatorHelpers { return states; } - // Permutes the given matrix according to the given permutation. - // If states is the current order of vector entries on which the given matrix - // acts, and permuted_states is the desired order of an array on which the - // permuted matrix should act, then the permutation is defined such that - // [states[i] for i in permutation] produces permuted_states. - static cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, std::vector permutation) { auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); std::vector> sorted_values; @@ -145,9 +143,11 @@ class _OperatorHelpers { return result; } - // Returns the degrees sorted in canonical order. - static std::vector canonicalize_degrees(std::vector degrees) { + std::vector canonicalize_degrees(std::vector degrees) { std::sort(degrees.begin(), degrees.end(), std::greater()); return degrees; } + }; +} +} diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h new file mode 100644 index 0000000000..5f336847a5 --- /dev/null +++ b/runtime/cudaq/dynamics/helpers.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include "cudaq/utils/tensor.h" + +namespace cudaq { +namespace detail { + + /// Generates all possible states for the given dimensions ordered according + /// to the sequence of degrees (ordering is relevant if dimensions differ). + std::vector generate_all_states(std::vector degrees, std::map dimensions); + + // Permutes the given matrix according to the given permutation. + // If states is the current order of vector entries on which the given matrix + // acts, and permuted_states is the desired order of an array on which the + // permuted matrix should act, then the permutation is defined such that + // [states[i] for i in permutation] produces permuted_states. + cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation); + + // Returns the degrees sorted in canonical order. + std::vector canonicalize_degrees(std::vector degrees); +} +} + diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 8c245f818d..26492f2ec5 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" -#include "helpers.cpp" +#include "helpers.h" namespace cudaq { @@ -15,7 +15,7 @@ std::vector MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::vector canon_degrees) { auto states = - _OperatorHelpers::generate_all_states(canon_degrees, m_dimensions); + cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; for (auto degree : op_degrees) @@ -45,12 +45,12 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::tuple> MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, std::vector op_degrees) { - auto canon_degrees = _OperatorHelpers::canonicalize_degrees(op_degrees); + auto canon_degrees = cudaq::detail::canonicalize_degrees(op_degrees); if (op_degrees == canon_degrees) return std::tuple>{op_matrix, canon_degrees}; auto permutation = this->_compute_permutation(op_degrees, canon_degrees); - auto result = _OperatorHelpers::permute_matrix(op_matrix, permutation); + auto result = cudaq::detail::permute_matrix(op_matrix, permutation); return std::tuple>{result, canon_degrees}; } diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 6a2973bb6c..2f32e565a5 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "helpers.h" #include #include @@ -180,8 +181,7 @@ std::vector operator_sum::degrees() const { unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); - std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION - return degrees; + return cudaq::detail::canonicalize_degrees(degrees); } template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 87c8237f2e..22f31a52c7 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" -#include "cudaq/dynamics/helpers.cpp" +#include "helpers.h" #include #include @@ -77,7 +77,7 @@ cudaq::matrix_2 product_operator::m_evaluate( noncanon_set.insert(degree); } } - auto degrees = _OperatorHelpers::canonicalize_degrees(noncanon_degrees); + auto degrees = cudaq::detail::canonicalize_degrees(noncanon_degrees); auto evaluated = EvaluatedMatrix(degrees, _padded_op(arithmetics, terms[0], degrees, dimensions, parameters)); @@ -139,8 +139,7 @@ std::vector product_operator::degrees() const { unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); - std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION - return degrees; + return cudaq::detail::canonicalize_degrees(degrees); } template diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index e2cd52e337..95ec494d29 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -17,9 +17,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } @@ -462,7 +465,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp index ac1caf4f10..117fe51f48 100644 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -17,9 +17,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 5f6bb391b5..f3e649cc80 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -17,9 +17,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 5c2f76be57..a16036e910 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -19,9 +19,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } @@ -112,6 +115,15 @@ void assert_product_equal(const cudaq::product_operator ASSERT_TRUE(got.get_terms() == expected_terms); } +void print(cudaq::matrix_2 matrix) { + for (std::size_t i = 0; i < matrix.get_rows(); i++) { + for (std::size_t j = 0; j < matrix.get_columns(); j++) { + std::cout << matrix[{i,j}] << " "; + } + std::cout << std::endl; + } +} + } // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { @@ -131,6 +143,14 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto matrix0 = utils_1::annihilate_matrix(level_count); auto matrix1 = utils_1::create_matrix(level_count); auto want_matrix = matrix0 * matrix1; + utils_1::print(matrix1); + std::cout << std::endl; + utils_1::print(matrix0); + std::cout << std::endl; + utils_1::print(want_matrix); + std::cout << std::endl; + utils_1::print(got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; @@ -147,7 +167,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -183,7 +203,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {0, 2}; + std::vector want_degrees = {2, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -220,7 +240,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {0, 2}; + std::vector want_degrees = {2, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -317,7 +337,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -355,7 +375,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -393,7 +413,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -431,7 +451,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -469,7 +489,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -507,7 +527,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -549,7 +569,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -591,7 +611,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -631,7 +651,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -667,7 +687,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. @@ -698,7 +718,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. @@ -731,7 +751,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(scalar_op.evaluate() == value_0); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. @@ -769,7 +789,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 2); - std::vector want_degrees = {0, 1, 2}; + std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); /// Check the matrices. From 93525b3e447badb08b728f3d76014adf95d1b623 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 31 Jan 2025 20:27:05 +0000 Subject: [PATCH 076/311] enabling a whole bunch of additional tests Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 22 +- runtime/cudaq/dynamics/definition.cpp | 13 +- runtime/cudaq/dynamics/matrix_operators.cpp | 106 +++--- runtime/cudaq/dynamics/operator_sum.cpp | 11 +- runtime/cudaq/dynamics/product_operators.cpp | 108 ++---- runtime/cudaq/operators.h | 28 +- unittests/dynamics/matrix_ops_arithmetic.cpp | 174 +++------ .../dynamics/product_operators_arithmetic.cpp | 339 +++++------------- 8 files changed, 252 insertions(+), 549 deletions(-) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index 444173d941..ccc394c9b9 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -143,32 +143,20 @@ class ScalarCallbackFunction : CallbackFunction { /// or scalar operator is instantiated by other means than the `define` /// class method. class Definition { -public: +private: std::string id; - - // The user-provided generator function should take a variable number of - // complex doubles for the parameters. It should return a - // `cudaq::tensor` type representing the operator - // matrix. CallbackFunction generator; + std::map m_expected_dimensions; - // Constructor. - Definition(); +public: - // Destructor. + Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create); + Definition(Definition &&def); ~Definition(); - void create_definition(const std::string &operator_id, - std::map expected_dimensions, - CallbackFunction &&create); - // To call the generator function matrix_2 generate_matrix( const std::map °rees, const std::map> ¶meters) const; - -private: - // Member variables - std::map m_expected_dimensions; }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp index cc357fbeab..0a9c1791b2 100644 --- a/runtime/cudaq/dynamics/definition.cpp +++ b/runtime/cudaq/dynamics/definition.cpp @@ -16,16 +16,11 @@ namespace cudaq { -Definition::Definition() = default; +Definition::Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create) + : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} -// Convenience setter -void Definition::create_definition(const std::string &operator_id, - std::map expected_dimensions, - CallbackFunction &&create) { - id = operator_id; - generator = std::move(create); - m_expected_dimensions = std::move(expected_dimensions); -} +Definition::Definition(Definition &&def) + : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( const std::map °rees, diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 4e7a7a6bcb..ee171fc7c1 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -18,11 +18,8 @@ std::map matrix_operator::m_ops = {}; product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -33,18 +30,16 @@ product_operator matrix_operator::identity(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, std::move(func)); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when @@ -53,18 +48,16 @@ product_operator matrix_operator::zero(int degree) { auto mat = matrix_2(dimension, dimension); return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -73,18 +66,16 @@ product_operator matrix_operator::annihilate(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::create(int degree) { std::string op_id = "create"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -93,18 +84,16 @@ product_operator matrix_operator::create(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::position(int degree) { std::string op_id = "position"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -117,18 +106,16 @@ product_operator matrix_operator::position(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -141,18 +128,16 @@ product_operator matrix_operator::momentum(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::number(int degree) { std::string op_id = "number"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -161,18 +146,16 @@ product_operator matrix_operator::number(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -181,18 +164,16 @@ product_operator matrix_operator::parity(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> parameters) { std::size_t dimension = dimensions[degree]; auto displacement_amplitude = parameters["displacement"]; @@ -207,19 +188,17 @@ product_operator matrix_operator::displace(int degree) { auto term2 = std::conj(displacement_amplitude) * annihilate; return (term1 - term2).exponential(); }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> parameters) { std::size_t dimension = dimensions[degree]; auto squeezing = parameters["squeezing"]; @@ -235,8 +214,9 @@ product_operator matrix_operator::squeeze(int degree) { auto difference = 0.5 * (term1 - term2); return difference.exponential(); }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } @@ -244,7 +224,11 @@ product_operator matrix_operator::squeeze(int degree) { matrix_2 matrix_operator::to_matrix( std::map dimensions, std::map> parameters) const { - return m_ops[id].generator(dimensions, parameters); + auto it = matrix_operator::m_ops.find(this->id); + if (it != matrix_operator::m_ops.end()) { + return it->second.generate_matrix(dimensions, parameters); + } + throw std::range_error("unable to find operator"); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 2f32e565a5..8fc83fdd32 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -52,23 +52,20 @@ cudaq::matrix_2 operator_sum::m_evaluate( auto sum = EvaluatedMatrix(); if (pad_terms) { - sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, dimensions, - parameters, pad_terms)); + sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; - auto eval = paddedTerm(term).m_evaluate(arithmetics, dimensions, - parameters, pad_terms); + auto eval = paddedTerm(term).m_evaluate(arithmetics, pad_terms); sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); } } else { sum = - EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, dimensions, - parameters, pad_terms)); + EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; auto eval = - term.m_evaluate(arithmetics, dimensions, parameters, pad_terms); + term.m_evaluate(arithmetics, pad_terms); sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); } } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 22f31a52c7..0489eb9a4c 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -13,102 +13,70 @@ #include #include #include +#include namespace cudaq { // private methods -cudaq::matrix_2 -_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::matrix_operator op, +EvaluatedMatrix +_padded_op(MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, std::vector degrees, std::map dimensions, std::map> parameters) { /// Creating the tensor product with op being last is most efficient. - std::vector padded; + std::vector padded; for (const auto °ree : degrees) { - if (std::find(op.degrees.begin(), op.degrees.end(), degree) == - op.degrees.end(), - degree) { - padded.push_back( - arithmetics.evaluate(cudaq::matrix_operator::identity(degree)) - .matrix()); + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == op.degrees.end()) { + // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS ARE REQUIRED. + padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); + // FIXME: avoid creation of a product here - + // but we need to make sure identity is defined before using it (all ops are lazily defined...) + //padded.push_back(cudaq::matrix_operator("identity", {degree}).to_matrix()); } - matrix_2 mat = op.to_matrix(dimensions, parameters); - padded.push_back(mat); } - /// FIXME: This directly uses cudaq::kronecker instead of the tensor method. - /// I need to double check to make sure this gives the equivalent behavior - /// to the method used in python. - return cudaq::kronecker(padded.begin(), padded.end()); - ; + matrix_2 mat = op.to_matrix(dimensions, parameters); + auto res = EvaluatedMatrix(op.degrees, mat); // FIXME: PUT THIS LAST + for(auto &op : padded) + res = arithmetics.tensor(res, op); + return res; } +// FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - +// IT SUPPOSED TO TAKE A TRANSFORMATION (ANY OPERATOR ARITHMETICS) AND APPLY IT template cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const { - /// Grab the underlying elementary operators. - auto terms = this->get_terms(); - - std::set noncanon_set; - for (const auto &op : terms) { - for (const auto °ree : op.degrees) { - noncanon_set.insert(degree); - } - } - std::vector noncanon_degrees(noncanon_set.begin(), noncanon_set.end()); - - // Calculate the total dimensions of the Hilbert space to create our - // identity matrix. - auto full_hilbert_size = 1; - for (const auto degree : noncanon_degrees) - full_hilbert_size *= dimensions[degree]; - cudaq::matrix_2 result(full_hilbert_size, full_hilbert_size); - // If this product operator consists only of scalar operator terms, - // we will avoid all of the below logic and just return the scalar value - // stored in an identity matrix spanning the full Hilbert space of the - // provided `dimensions`. + MatrixArithmetics arithmetics, bool pad_terms) const { + const std::vector &terms = this->terms[0]; + auto degrees = this->degrees(); + cudaq::matrix_2 result; + if (terms.size() > 0) { if (pad_terms) { - // Sorting the degrees to avoid unnecessary permutations during the - // padding. - std::set noncanon_set; - for (const auto &op : terms) { - for (const auto °ree : op.degrees) { - noncanon_set.insert(degree); - } - } - auto degrees = cudaq::detail::canonicalize_degrees(noncanon_degrees); - auto evaluated = - EvaluatedMatrix(degrees, _padded_op(arithmetics, terms[0], - degrees, dimensions, parameters)); - + auto evaluated = _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto op = terms[op_idx]; - if (op.degrees.size() != 1) { - auto padded_op_to_print = - _padded_op(arithmetics, op, degrees, dimensions, parameters); - auto padded_mat = - EvaluatedMatrix(degrees, _padded_op(arithmetics, op, degrees, - dimensions, parameters)); - evaluated = arithmetics.mul(evaluated, padded_mat); + const HandlerTy &op = terms[op_idx]; + if (op.degrees.size() != 1 || op != cudaq::matrix_operator("identity", op.degrees)) { + auto padded = _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = arithmetics.mul(evaluated, padded); } } result = evaluated.matrix(); } else { auto evaluated = arithmetics.evaluate(terms[0]); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto op = terms[op_idx]; - auto mat = op.to_matrix(dimensions, parameters); - evaluated = - arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + auto &op = terms[op_idx]; + auto mat = op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); } result = evaluated.matrix(); } } else { + auto full_hilbert_size = 1; + for (const auto degree : degrees) + full_hilbert_size *= arithmetics.m_dimensions[degree]; result = cudaq::matrix_2::identity(full_hilbert_size); } - auto coefficient = this->get_coefficient(); - return coefficient.evaluate(parameters) * result; + return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } template @@ -159,8 +127,7 @@ scalar_operator product_operator::get_coefficient() const { template cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const; + MatrixArithmetics arithmetics, bool pad_terms) const; template std::vector product_operator::degrees() const; @@ -276,10 +243,7 @@ std::string product_operator::to_string() const { template matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const { - if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) - return this->m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, - parameters); - return this->get_terms()[0].to_matrix(dimensions, parameters); + return this->m_evaluate(MatrixArithmetics(dimensions, parameters)); } template diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 726d8f64a8..c50f3df218 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -331,8 +331,7 @@ friend class operator_sum; // FIXME: explicitly list members instead? template void aggregate_terms(const HandlerTy &head, Args&& ... args); - matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; public: @@ -470,14 +469,6 @@ class matrix_operator { private: static std::map m_ops; -protected: - // FIXME: revise implementation - /// @brief The number of levels, that is the dimension, for each degree of - /// freedom in canonical order that the operator acts on. A value of zero or - /// less indicates that the operator is defined for any dimension of that - /// degree. - std::map expected_dimensions; - public: // The constructor should never be called directly by the user: // Keeping it internally documented for now, however. @@ -579,16 +570,14 @@ class matrix_operator { /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - template - void define(std::string operator_id, std::map expected_dimensions, - Func create) { - if (matrix_operator::m_ops.find(operator_id) != matrix_operator::m_ops.end()) { + static void define(std::string operator_id, std::map expected_dimensions, + CallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); + auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); + if (!result.second) { // todo: make a nice error message to say op already exists throw; } - auto defn = Definition(); - defn.create_definition(operator_id, expected_dimensions, create); - matrix_operator::m_ops[operator_id] = defn; } }; @@ -690,14 +679,15 @@ friend class MatrixArithmetics; /// of an operator expression. class MatrixArithmetics : public OperatorArithmetics { private: - std::map m_dimensions; - std::map> m_parameters; std::vector _compute_permutation(std::vector op_degrees, std::vector canon_degrees); std::tuple> _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); public: + std::map &m_dimensions; // fixme: make const + std::map> &m_parameters; // fixme: make const + MatrixArithmetics(std::map dimensions, std::map> parameters) : m_dimensions(dimensions), m_parameters(parameters) {} diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 95ec494d29..ede78fac53 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -206,17 +206,15 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator + scalar_operator` @@ -230,18 +228,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, - // {{"value", const_scale_factor}}); - // auto got_reverse_matrix = reverse.to_matrix( - // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::parity_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator - scalar_operator` @@ -249,28 +243,19 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::matrix_operator::number(0); auto other = cudaq::scalar_operator(const_scale_factor); - // Produces an `operator_sum` type. auto sum = self - other; auto reverse = other - self; ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}); - // auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = - scaled_identity - utils_0::number_matrix(level_count); - // std::cout << "\nwant = \n" << want_matrix.dump() << "\n"; - // std::cout << "\ngot = \n" << got_matrix.dump() << "\n"; - // std::cout << "\nwant reverse = \n" << want_reverse_matrix.dump() << "\n"; - // std::cout << "\ngot reverse = \n" << got_reverse_matrix.dump() << "\n"; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils_0::number_matrix(level_count); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator - scalar_operator` @@ -284,19 +269,13 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, - // {{"value", const_scale_factor}}); - // auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {{"value", - // const_scale_factor}}); auto want_matrix = - // utils_0::position_matrix(level_count) + scaled_identity; - auto want_reverse_matrix = - scaled_identity + utils_0::position_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils_0::position_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils_0::position_matrix(level_count); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator * scalar_operator` @@ -304,7 +283,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::matrix_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); - // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; @@ -315,8 +293,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); auto got_matrix = product.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); @@ -332,7 +308,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::matrix_operator::create(0); auto other = cudaq::scalar_operator(function); - // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; @@ -343,18 +318,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = product.to_matrix({{degree_index, level_count}}, - // {{"value", const_scale_factor}}); - // auto got_reverse_matrix = reverse.to_matrix( - // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = scaled_identity * utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } } @@ -369,12 +340,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::matrix_operator::annihilate(0); auto other = cudaq::matrix_operator::create(0); - // Produces an `operator_sum` type. auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. - auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + utils_0::create_matrix(level_count); @@ -389,15 +357,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}); + //auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full + create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + //utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, same DOF. @@ -408,13 +375,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) - utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, different DOF's. @@ -425,16 +389,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. - auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}); + //auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full - create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + //utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, same DOF. @@ -448,12 +410,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); - // /// Check the matrices. - - // auto got_matrix = product.to_matrix({{0, level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) * utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, different DOF's. @@ -468,17 +428,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + //auto got_matrix = product.to_matrix({{0, level_count}}, {}); auto want_matrix = annihilate_full * create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + //utils_0::checkEqual(want_matrix, got_matrix); } } @@ -494,7 +451,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -504,8 +460,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(got.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -513,20 +467,18 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - // auto got_reverse_matrix = - // reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } /// `matrix_operator - operator_sum` and `operator_sum - /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -536,9 +488,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(got.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -546,20 +495,18 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } /// `matrix_operator * operator_sum` and `operator_sum * /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::squeeze(0) + cudaq::matrix_operator::identity(1); @@ -573,9 +520,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { for (auto &term : reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = @@ -585,13 +529,12 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {{"squeezing", value}}); auto got_reverse_matrix = reverse.to_matrix({{0, - // level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } /// `operator_sum += matrix_operator` @@ -602,9 +545,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(operator_sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::displace_matrix(level_count, value)); @@ -613,10 +553,9 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {{"displacement", value}}); + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } /// `operator_sum -= matrix_operator` @@ -627,9 +566,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(operator_sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -637,16 +573,14 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = term_0_full + term_1_full - self_full; - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } /// `operator_sum *= matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -656,9 +590,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { for (auto &term : operator_sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -667,9 +598,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = sum_full * self_full; - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index a16036e910..18e26be328 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -143,15 +143,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto matrix0 = utils_1::annihilate_matrix(level_count); auto matrix1 = utils_1::create_matrix(level_count); auto want_matrix = matrix0 * matrix1; - utils_1::print(matrix1); - std::cout << std::endl; - utils_1::print(matrix0); - std::cout << std::endl; - utils_1::print(want_matrix); - std::cout << std::endl; - utils_1::print(got_matrix); - - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; ASSERT_TRUE(got.degrees() == want_degrees); @@ -171,25 +163,20 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // got.to_matrix({{0, level_count}, {1, level_count}}, {}); - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - // auto identity = utils_1::id_matrix(level_count); - // auto matrix0 = utils_1::annihilate_matrix(level_count); - // auto matrix1 = utils_1::create_matrix(level_count); + auto identity = utils_1::id_matrix(level_count); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); - // auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - // auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - // auto want_matrix = fullHilbert0 * fullHilbert1; - // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -207,14 +194,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, - // {}); - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0,level_count},{2,level_count}}, - // {}); + //auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -225,8 +206,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -244,21 +225,13 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, - // {}); + //auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); auto matrix1 = utils_1::create_matrix(level_count); - /// Identity pad the operators to compute the kronecker - /// product to the full hilbert space. std::vector matrices_0; std::vector matrices_1; matrices_0 = {identity, identity, matrix0}; @@ -271,8 +244,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(got_matrix, want_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(got_matrix, want_matrix); } } } @@ -341,27 +314,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), utils_1::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = value_0 * utils_1::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + double` @@ -379,13 +346,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -397,8 +359,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + scalar_operator` @@ -417,13 +379,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -436,8 +393,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - complex` @@ -455,13 +412,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -474,8 +426,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - double` @@ -493,13 +445,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -511,8 +458,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - scalar_operator` @@ -531,13 +478,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::momentum_matrix(level_count)); @@ -550,8 +492,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * complex` @@ -573,13 +515,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -592,8 +529,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * double` @@ -615,13 +552,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse_reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::parity_matrix(level_count)); @@ -633,8 +565,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * scalar_operator` @@ -655,13 +587,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::position_matrix(level_count)); @@ -674,8 +601,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator *= complex` @@ -690,11 +617,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -706,7 +629,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= double` @@ -721,11 +644,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -736,7 +655,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= scalar_operator` @@ -754,11 +673,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -770,7 +685,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } } @@ -792,11 +707,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); + //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); // Build up each individual term, cast to the full Hilbert space of the // system. @@ -826,7 +737,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix + term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` @@ -840,15 +751,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(difference.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}, - // {2,level_count+1}}, {}); + //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -875,7 +779,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix - term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` @@ -889,15 +793,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(product.n_terms() == 4); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, - // {}); + //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -924,7 +821,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` @@ -938,12 +835,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(term_0.n_terms() == 4); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, - // {}); + //auto got_matrix = term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); // Build up each individual term, cast to the full Hilbert space of the // system. @@ -973,7 +865,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } } @@ -993,13 +885,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1012,8 +899,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix + elementary_matrix; auto want_matrix_reverse = elementary_matrix + product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - matrix_operator` @@ -1028,13 +915,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1047,8 +929,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix - elementary_matrix; auto want_matrix_reverse = elementary_matrix - product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * matrix_operator` @@ -1063,13 +945,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1082,8 +959,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix * elementary_matrix; auto want_matrix_reverse = elementary_matrix * product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator *= matrix_operator` @@ -1096,11 +973,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1112,7 +985,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix * elementary_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } } @@ -1133,14 +1006,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); + //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1166,8 +1033,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix + sum_matrix; auto want_matrix_reverse = sum_matrix + product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` @@ -1183,14 +1050,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); + //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1216,8 +1077,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix - difference_matrix; auto want_matrix_reverse = difference_matrix - product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -1233,14 +1094,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); + //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1266,7 +1121,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix * sum_matrix; auto want_matrix_reverse = sum_matrix * product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } From 15634ff821162fbbdbc720a13a5d65471c4761e0 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 15:27:17 +0000 Subject: [PATCH 077/311] some more product op fixes Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 22 +++---- runtime/cudaq/dynamics/definition.cpp | 6 +- runtime/cudaq/dynamics/helpers.cpp | 1 - runtime/cudaq/dynamics/manipulation.cpp | 22 ++++--- runtime/cudaq/dynamics/matrix_operators.cpp | 66 ++++++++++--------- runtime/cudaq/operators.h | 5 +- unittests/dynamics/matrix_ops_arithmetic.cpp | 19 +++--- .../dynamics/product_operators_arithmetic.cpp | 12 ++-- 8 files changed, 79 insertions(+), 74 deletions(-) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index ccc394c9b9..1421e92dc0 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -20,16 +20,14 @@ namespace cudaq { -// Limit the signature of the users callback function to accept a vector of ints -// for the degree of freedom dimensions, and a vector of complex doubles for the -// concrete parameter values. using Func = std::function, std::map>)>; + std::vector, std::map>)>; class CallbackFunction { private: - // The user provided callback function that takes the degrees of - // freedom and a vector of complex parameters. + // The user provided callback function that takes a vector defining the + // dimension for each degree of freedom it acts on, and a map of complex + // parameters. Func _callback_func; public: @@ -38,7 +36,7 @@ class CallbackFunction { template CallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v, + std::is_invocable_r_v, std::map>>, "Invalid callback function. Must have signature " "matrix_2(" @@ -76,9 +74,9 @@ class CallbackFunction { bool operator!() { return (!_callback_func); } matrix_2 - operator()(std::map degrees, + operator()(std::vector relevant_dimensions, std::map> parameters) const { - return _callback_func(std::move(degrees), std::move(parameters)); + return _callback_func(std::move(relevant_dimensions), std::move(parameters)); } }; @@ -146,17 +144,17 @@ class Definition { private: std::string id; CallbackFunction generator; - std::map m_expected_dimensions; + std::vector m_expected_dimensions; public: - Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create); + Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create); Definition(Definition &&def); ~Definition(); // To call the generator function matrix_2 generate_matrix( - const std::map °rees, + const std::vector &relevant_dimensions, const std::map> ¶meters) const; }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp index 0a9c1791b2..c285a788ce 100644 --- a/runtime/cudaq/dynamics/definition.cpp +++ b/runtime/cudaq/dynamics/definition.cpp @@ -16,16 +16,16 @@ namespace cudaq { -Definition::Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create) +Definition::Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create) : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} Definition::Definition(Definition &&def) : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( - const std::map °rees, + const std::vector &relevant_dimensions, const std::map> ¶meters) const { - return generator(degrees, parameters); + return generator(relevant_dimensions, parameters); } Definition::~Definition() = default; diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index c5fcce1cbe..f8ab19f138 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -111,7 +111,6 @@ class _OperatorHelpers { } for (auto degree = degrees.begin() + 1; degree != degrees.end(); ++degree) { - std::string term; std::vector result; for (auto current : states) { for (auto state = 0; state < dimensions[degrees[*degree]]; state++) { diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 26492f2ec5..1acbbbeb6c 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -18,9 +18,10 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; - for (auto degree : op_degrees) - reordering.push_back(canon_degrees[degree]); - + for (auto degree : op_degrees) { + auto it = std::find(canon_degrees.begin(), canon_degrees.end(), degree); + reordering.push_back(it - canon_degrees.begin()); + } std::vector result; for (auto state : states) { int index; @@ -29,9 +30,7 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, term += state[i]; } auto it = std::find(states.begin(), states.end(), term); - if (it != states.end()) - index = std::distance(states.begin(), it); - result.push_back(index); + result.push_back(it - states.begin()); } return result; @@ -60,9 +59,14 @@ EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, // assert len(frozenset(op1.degrees).intersection(op2.degrees)) == 0, \ // "Operators should not have common degrees of freedom." - auto op_degrees = op1.m_degrees; - std::copy(op2.m_degrees.begin(), op2.m_degrees.end(), - back_inserter(op_degrees)); + auto op1_deg = std::move(op1.degrees()); + auto op2_deg = std::move(op2.degrees()); + std::vector op_degrees; + op_degrees.reserve(op1_deg.size() + op2_deg.size()); + for (auto d : op1_deg) + op_degrees.push_back(d); + for (auto d : op2_deg) + op_degrees.push_back(d); auto op_matrix = cudaq::kronecker(op1.m_matrix, op2.m_matrix); auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); return EvaluatedMatrix(new_degrees, new_matrix); diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index ee171fc7c1..b8926ba470 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -19,9 +19,9 @@ std::map matrix_operator::m_ops = {}; product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // Build up the identity matrix. @@ -30,7 +30,7 @@ product_operator matrix_operator::identity(int degree) { } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, std::move(func)); + matrix_operator::define(op_id, {-1}, std::move(func)); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -39,16 +39,16 @@ product_operator matrix_operator::identity(int degree) { product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when // the user invokes this later on via, e.g, `to_matrix()`. - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -57,16 +57,16 @@ product_operator matrix_operator::zero(int degree) { product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -75,16 +75,16 @@ product_operator matrix_operator::annihilate(int degree) { product_operator matrix_operator::create(int degree) { std::string op_id = "create"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -93,9 +93,9 @@ product_operator matrix_operator::create(int degree) { product_operator matrix_operator::position(int degree) { std::string op_id = "position"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -106,7 +106,7 @@ product_operator matrix_operator::position(int degree) { } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -115,9 +115,9 @@ product_operator matrix_operator::position(int degree) { product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -128,7 +128,7 @@ product_operator matrix_operator::momentum(int degree) { } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -137,16 +137,16 @@ product_operator matrix_operator::momentum(int degree) { product_operator matrix_operator::number(int degree) { std::string op_id = "number"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = static_cast(i) + 0.0j; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -155,16 +155,16 @@ product_operator matrix_operator::number(int degree) { product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -173,9 +173,9 @@ product_operator matrix_operator::parity(int degree) { product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> parameters) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto displacement_amplitude = parameters["displacement"]; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); @@ -188,7 +188,7 @@ product_operator matrix_operator::displace(int degree) { auto term2 = std::conj(displacement_amplitude) * annihilate; return (term1 - term2).exponential(); }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -198,9 +198,9 @@ product_operator matrix_operator::displace(int degree) { product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> parameters) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto squeezing = parameters["squeezing"]; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); @@ -214,7 +214,7 @@ product_operator matrix_operator::squeeze(int degree) { auto difference = 0.5 * (term1 - term2); return difference.exponential(); }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -226,7 +226,11 @@ matrix_2 matrix_operator::to_matrix( std::map> parameters) const { auto it = matrix_operator::m_ops.find(this->id); if (it != matrix_operator::m_ops.end()) { - return it->second.generate_matrix(dimensions, parameters); + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->degrees.size()); + for (auto d : this->degrees) + relevant_dimensions.push_back(dimensions[d]); + return it->second.generate_matrix(relevant_dimensions, parameters); } throw std::range_error("unable to find operator"); } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index c50f3df218..b450f77e44 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -154,8 +154,7 @@ class operator_sum { std::tuple, std::vector> m_canonical_terms() const; - matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; void aggregate_terms(); @@ -570,7 +569,7 @@ class matrix_operator { /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - static void define(std::string operator_id, std::map expected_dimensions, + static void define(std::string operator_id, std::vector expected_dimensions, CallbackFunction &&create) { auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index ede78fac53..5acde43b42 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -334,6 +334,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { /// Keeping this fixed throughout. int level_count = 3; + std::map dimensions = {{0, level_count}, {1, level_count}}; // Addition, same DOF. { @@ -343,7 +344,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = utils_0::annihilate_matrix(level_count) + utils_0::create_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); @@ -362,9 +363,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - //auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = annihilate_full + create_full; - //utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, same DOF. @@ -375,7 +376,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = utils_0::annihilate_matrix(level_count) - utils_0::create_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); @@ -394,9 +395,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - //auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = annihilate_full - create_full; - //utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, same DOF. @@ -410,7 +411,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0, level_count}}); + auto got_matrix = product.to_matrix(dimensions); auto want_matrix = utils_0::annihilate_matrix(level_count) * utils_0::create_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); @@ -433,9 +434,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - //auto got_matrix = product.to_matrix({{0, level_count}}, {}); + auto got_matrix = product.to_matrix(dimensions); auto want_matrix = annihilate_full * create_full; - //utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 18e26be328..7d2fe8ee4e 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -194,8 +194,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - //auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -206,8 +206,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -225,8 +225,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - //auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); - //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); From 1336adae97841319f95bf93ff72afc7f3b7be1ff Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 16:00:03 +0000 Subject: [PATCH 078/311] just some test fixes Signed-off-by: Bettina Heim --- .../dynamics/product_operators_arithmetic.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 7d2fe8ee4e..c2a71618c8 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -234,8 +234,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector matrices_0; std::vector matrices_1; - matrices_0 = {identity, identity, matrix0}; - matrices_1 = {matrix1, identity, identity}; + matrices_0 = {identity, matrix0}; + matrices_1 = {matrix1, identity}; auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); @@ -244,8 +244,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(got_matrix, want_matrix); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(got_matrix, want_matrix); } } } @@ -660,8 +660,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= scalar_operator` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::momentum(1); auto scalar_op = cudaq::scalar_operator(value_0); product *= scalar_op; @@ -684,8 +684,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { value_0 * utils_1::id_matrix(level_count * level_count); auto want_matrix = product_matrix * scaled_identity; - - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } } From f4ea5b5cc3c2eb7c86ab5d569cbd40427b441744 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 22:12:02 +0000 Subject: [PATCH 079/311] fixing matrix reordering issues Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/helpers.cpp | 4 +- runtime/cudaq/dynamics/manipulation.cpp | 13 ++++--- runtime/cudaq/dynamics/operator_sum.cpp | 29 ++++----------- .../dynamics/product_operators_arithmetic.cpp | 37 +++++++++---------- 4 files changed, 35 insertions(+), 48 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index f8ab19f138..2eab4ec238 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -110,10 +110,10 @@ class _OperatorHelpers { states.push_back(std::to_string(state)); } - for (auto degree = degrees.begin() + 1; degree != degrees.end(); ++degree) { + for (auto idx = 1; idx < degrees.size(); ++idx) { std::vector result; for (auto current : states) { - for (auto state = 0; state < dimensions[degrees[*degree]]; state++) { + for (auto state = 0; state < dimensions[degrees[idx]]; state++) { result.push_back(current + std::to_string(state)); } } diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 1acbbbeb6c..5b713331a7 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -22,18 +22,21 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, auto it = std::find(canon_degrees.begin(), canon_degrees.end(), degree); reordering.push_back(it - canon_degrees.begin()); } - std::vector result; + + std::vector op_states = + cudaq::detail::generate_all_states(op_degrees, m_dimensions); + + std::vector permutation; for (auto state : states) { - int index; std::string term; for (auto i : reordering) { term += state[i]; } - auto it = std::find(states.begin(), states.end(), term); - result.push_back(it - states.begin()); + auto it = std::find(op_states.begin(), op_states.end(), term); + permutation.push_back(it - op_states.begin()); } - return result; + return permutation; } // Given a matrix representation that acts on the given degrees or freedom, diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 8fc83fdd32..858a4ec575 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -20,18 +20,10 @@ namespace cudaq { template cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const { + MatrixArithmetics arithmetics, bool pad_terms) const { auto terms = this->get_terms(); - - std::set degrees_set; - for (auto op : terms) { - for (auto degree : op.degrees()) { - degrees_set.insert(degree); - } - } - std::vector degrees(degrees_set.begin(), degrees_set.end()); + auto degrees = this->degrees(); // We need to make sure all matrices are of the same size to sum them up. auto paddedTerm = [&](auto &&term) { @@ -51,13 +43,11 @@ cudaq::matrix_2 operator_sum::m_evaluate( auto sum = EvaluatedMatrix(); if (pad_terms) { - - sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, pad_terms)); + auto padded_term = paddedTerm(terms[0]); + sum = EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { - auto term = terms[term_idx]; - - auto eval = paddedTerm(term).m_evaluate(arithmetics, pad_terms); - sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + padded_term = paddedTerm(terms[term_idx]); + sum = arithmetics.add(sum, EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms))); } } else { sum = @@ -148,8 +138,7 @@ void operator_sum::aggregate_terms(const product_operator template cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const; + MatrixArithmetics arithmetics, bool pad_terms) const; template std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; @@ -301,9 +290,7 @@ std::string operator_sum::to_string() const { template matrix_2 operator_sum::to_matrix(const std::map &dimensions, const std::map> ¶meters) const { - /// FIXME: Not doing any conversion to spin op yet. - return m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, - parameters); + return m_evaluate(MatrixArithmetics(dimensions, parameters)); } template diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index c2a71618c8..b3cde934f6 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -691,6 +691,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; + std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; // `product_operator + product_operator` { @@ -706,10 +707,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); - //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); + auto got_matrix = sum.to_matrix(dimensions); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -732,11 +731,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix + term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` @@ -750,7 +749,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(difference.n_terms() == 2); - //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); + auto got_matrix = difference.to_matrix(dimensions); std::vector matrices_0_0; std::vector matrices_0_1; @@ -774,11 +773,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix - term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` @@ -792,7 +791,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(product.n_terms() == 4); - //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); + auto got_matrix = product.to_matrix(dimensions); std::vector matrices_0_0; std::vector matrices_0_1; @@ -816,11 +815,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` @@ -834,10 +833,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(term_0.n_terms() == 4); - //auto got_matrix = term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); + auto got_matrix = term_0.to_matrix(dimensions); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -860,11 +857,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } } From 1cd4ffda4c70d0a3f7147007f908f5fa85c259f5 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 23:49:40 +0000 Subject: [PATCH 080/311] enabling the rest of the tests Signed-off-by: Bettina Heim --- unittests/dynamics/operator_sum.cpp | 402 ++++++------------ .../dynamics/product_operators_arithmetic.cpp | 25 +- 2 files changed, 151 insertions(+), 276 deletions(-) diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index f3e649cc80..abbe10a52f 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -257,13 +257,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - /// Check the matrices. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = - // product.to_matrix({{1, level_count}, {2, level_count + 1}}); - // auto got_matrix_reverse = - // reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -275,8 +270,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - //utils_2::checkEqual(want_matrix, got_matrix); - //utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` @@ -291,26 +286,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, - // {2,level_count+1}}); - - // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - // utils_2::create_matrix(level_count)); - // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - // utils_2::id_matrix(level_count)); - // auto sum_matrix = matrix0 + matrix1; - // auto scaled_identity = - // value * utils_2::id_matrix((level_count) * (level_count + 1)); - - // auto want_matrix = sum_matrix + scaled_identity; - // auto want_matrix_reverse = scaled_identity + sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2,level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix_reverse = scaled_identity + sum_matrix; + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -324,26 +315,21 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = difference.to_matrix({{1, level_count}, {2, - // level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, - // level_count}, {2, level_count+1}}); - - // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - // utils_2::create_matrix(level_count)); - // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - // utils_2::id_matrix(level_count)); - // auto sum_matrix = matrix0 + matrix1; - // auto scaled_identity = - // value * utils_2::id_matrix((level_count) * (level_count + 1)); - - // auto want_matrix = sum_matrix - scaled_identity; - // auto want_matrix_reverse = scaled_identity - sum_matrix; - // // utils_2::checkEqual(want_matrix, got_matrix); - // // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= scalar_operator` @@ -359,29 +345,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. - // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, - // {2, - // // level_count+1}}); - - // std::vector matrices_1 = { - // utils_2::id_matrix(level_count + 1), - // utils_2::create_matrix(level_count), - // utils_2::id_matrix(level_count)}; - // std::vector matrices_2 = { - // utils_2::momentum_matrix(level_count + 1), - // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; - // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - // auto scaled_identity = - // value * - // utils_2::id_matrix((level_count + 1) * level_count * level_count); - - // auto want_matrix = (matrix0 + matrix1) * scaled_identity; - // // utils_2::checkEqual(want_matrix, got_matrix); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + + std::vector matrices_1 = { + utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)}; + std::vector matrices_2 = { + utils_2::momentum_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + + auto want_matrix = (matrix0 + matrix1) * scaled_identity; + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += scalar_operator` @@ -393,29 +370,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. - // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, - // {2, - // // level_count+1}}); - - // std::vector matrices_1 = { - // utils_2::id_matrix(level_count + 1), - // utils_2::parity_matrix(level_count), - // utils_2::id_matrix(level_count)}; - // std::vector matrices_2 = { - // utils_2::position_matrix(level_count + 1), - // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; - // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - // auto scaled_identity = - // value * - // utils_2::id_matrix((level_count + 1) * level_count * level_count); - - // auto want_matrix = matrix0 + matrix1 + scaled_identity; - // // utils_2::checkEqual(want_matrix, got_matrix); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + + std::vector matrices_1 = { + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + std::vector matrices_2 = { + utils_2::position_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + + auto want_matrix = matrix0 + matrix1 + scaled_identity; + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= scalar_operator` @@ -427,29 +395,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. - // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, - // {2, - // // level_count+1}}); - - // std::vector matrices_1 = { - // utils_2::id_matrix(level_count + 1), - // utils_2::number_matrix(level_count), - // utils_2::id_matrix(level_count)}; - // std::vector matrices_2 = { - // utils_2::annihilate_matrix(level_count + 1), - // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; - // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - // auto scaled_identity = - // value * - // utils_2::id_matrix((level_count + 1) * level_count * level_count); - - // auto want_matrix = (matrix0 + matrix1) - scaled_identity; - // // utils_2::checkEqual(want_matrix, got_matrix); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + + std::vector matrices_1 = { + utils_2::id_matrix(level_count + 1), + utils_2::number_matrix(level_count)}; + std::vector matrices_2 = { + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + + auto want_matrix = (matrix0 + matrix1) - scaled_identity; + utils_2::checkEqual(want_matrix, got_matrix); } } @@ -479,14 +438,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = product.to_matrix({{1, level_count}, {2, - // level_count+1}}, - // {}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, - // level_count+1}}); + auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -498,8 +451,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + double` and `double + operator_sum` @@ -513,13 +466,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, - // level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -531,8 +479,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - double` and `double - operator_sum` @@ -546,13 +494,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, - // level_count+1}}); + auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::parity_matrix(level_count)); @@ -564,8 +507,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= double` @@ -581,12 +524,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -599,7 +537,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { double_value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += double` @@ -611,11 +549,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -627,7 +561,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { double_value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= double` @@ -639,11 +573,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -655,7 +585,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { double_value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * std::complex` and `std::complex * @@ -680,13 +610,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = product.to_matrix({{1,level_count}, {2, - // level_count+1}}); auto got_matrix_reverse = - // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = product.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -698,8 +623,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + std::complex` and `std::complex + @@ -714,13 +639,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, - // level_count+1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -732,8 +652,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - std::complex` and `std::complex - @@ -748,13 +668,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = difference.to_matrix({{1,level_count}, {2, - // level_count+1}}); auto got_matrix_reverse = - // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -766,8 +681,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= std::complex` @@ -783,12 +698,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {{"displacement", value}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"displacement", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -800,7 +710,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += std::complex` @@ -812,12 +722,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -829,7 +734,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= std::complex` @@ -841,12 +746,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::position_matrix(level_count)); @@ -857,7 +757,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } } @@ -876,11 +776,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 5); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, - // level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -918,7 +814,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix + sum_1_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum - operator_sum` @@ -933,11 +829,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 5); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = difference.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -975,7 +867,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix - sum_1_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * operator_sum` @@ -996,13 +888,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum_product_reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); auto - // got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -1041,8 +928,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto want_matrix = sum_0_matrix * sum_1_matrix; auto want_matrix_reverse = sum_1_matrix * sum_0_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= operator_sum` @@ -1059,11 +946,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -1101,7 +984,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix * sum_1_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } } @@ -1122,11 +1005,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, - // {2, level_count+2}}, {}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1142,17 +1021,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix + product_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= product_operator` @@ -1166,11 +1046,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, - // {2, level_count+2}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1186,17 +1062,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix - product_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum *= product_operator` @@ -1213,11 +1090,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(term.n_terms() == 3); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, - // {2, level_count+2}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1233,16 +1106,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix * product_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } } \ No newline at end of file diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index b3cde934f6..cb4838fd6e 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -988,6 +988,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { int level_count = 3; + std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; // `product_operator + operator_sum` { @@ -1002,8 +1003,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + auto got_matrix = sum.to_matrix(dimensions); + auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1029,8 +1030,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix + sum_matrix; auto want_matrix_reverse = sum_matrix + product_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` @@ -1046,8 +1047,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + auto got_matrix = difference.to_matrix(dimensions); + auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1073,8 +1074,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix - difference_matrix; auto want_matrix_reverse = difference_matrix - product_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -1090,8 +1091,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + auto got_matrix = product.to_matrix(dimensions); + auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1117,7 +1118,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix * sum_matrix; auto want_matrix_reverse = sum_matrix * product_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } From f7a9d1b3e207e459bea9322c5be13f6bf7f051c0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 09:11:01 -0800 Subject: [PATCH 081/311] Adding template parameter HandlerTy for operator_sum and formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 15 +- runtime/cudaq/cudm_helpers.h | 4 +- runtime/cudaq/definition.h | 21 +- runtime/cudaq/dynamics/definition.cpp | 12 +- runtime/cudaq/dynamics/helpers.cpp | 35 +- runtime/cudaq/dynamics/helpers.h | 32 +- runtime/cudaq/dynamics/manipulation.cpp | 11 +- runtime/cudaq/dynamics/matrix_operators.cpp | 34 +- runtime/cudaq/dynamics/operator_sum.cpp | 875 +++++++++--------- runtime/cudaq/dynamics/product_operators.cpp | 551 ++++++----- runtime/cudaq/dynamics/scalar_operators.cpp | 88 +- runtime/cudaq/dynamics/templates.h | 245 +++-- runtime/cudaq/operators.h | 388 ++++---- runtime/cudaq/utils/tensor.h | 2 +- unittests/dynamics/matrix_ops_arithmetic.cpp | 102 +- unittests/dynamics/matrix_ops_simple.cpp | 10 +- unittests/dynamics/operator_sum.cpp | 226 +++-- .../dynamics/product_operators_arithmetic.cpp | 167 ++-- 18 files changed, 1556 insertions(+), 1262 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 31d82ea201..1abe004c0d 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,7 +16,7 @@ #include namespace cudaq { -template +template class BaseIntegrator { protected: std::map integrator_options; @@ -24,9 +24,9 @@ class BaseIntegrator { double t; std::map dimensions; std::shared_ptr schedule; - std::shared_ptr hamiltonian; + std::shared_ptr> hamiltonian; std::shared_ptr> stepper; - std::vector> collapse_operators; + std::vector>> collapse_operators; virtual void post_init() = 0; @@ -61,10 +61,11 @@ class BaseIntegrator { } /// @brief Set the system parameters (dimensions, schedule, and operators) - void set_system( - const std::map &dimensions, std::shared_ptr schedule, - std::shared_ptr hamiltonian, - std::vector> collapse_operators = {}) { + void set_system(const std::map &dimensions, + std::shared_ptr schedule, + std::shared_ptr> hamiltonian, + std::vector>> + collapse_operators = {}) { this->dimensions = dimensions; this->schedule = schedule; this->hamiltonian = hamiltonian; diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 6ed2fc087b..36a1ba9688 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -25,10 +25,12 @@ compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents); +template cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, - const operator_sum &op, const std::vector &mode_extents); + const operator_sum &op, + const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index 1421e92dc0..49decefeb6 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -25,8 +25,8 @@ using Func = std::function relevant_dimensions, std::map> parameters) const { - return _callback_func(std::move(relevant_dimensions), std::move(parameters)); + return _callback_func(std::move(relevant_dimensions), + std::move(parameters)); } }; @@ -114,7 +115,7 @@ class ScalarCallbackFunction : CallbackFunction { } // assignment operator - ScalarCallbackFunction& operator=(const ScalarCallbackFunction &other) { + ScalarCallbackFunction &operator=(const ScalarCallbackFunction &other) { if (this != &other) { _callback_func = other._callback_func; } @@ -122,7 +123,7 @@ class ScalarCallbackFunction : CallbackFunction { } // move assignment operator - ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other) { + ScalarCallbackFunction &operator=(ScalarCallbackFunction &&other) { if (this != &other) { _callback_func = std::move(other._callback_func); } @@ -141,14 +142,14 @@ class ScalarCallbackFunction : CallbackFunction { /// or scalar operator is instantiated by other means than the `define` /// class method. class Definition { -private: +private: std::string id; CallbackFunction generator; std::vector m_expected_dimensions; public: - - Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create); + Definition(const std::string &operator_id, + std::vector expected_dimensions, CallbackFunction &&create); Definition(Definition &&def); ~Definition(); diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp index c285a788ce..61e83d0a52 100644 --- a/runtime/cudaq/dynamics/definition.cpp +++ b/runtime/cudaq/dynamics/definition.cpp @@ -16,11 +16,15 @@ namespace cudaq { -Definition::Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create) - : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} +Definition::Definition(const std::string &operator_id, + std::vector expected_dimensions, + CallbackFunction &&create) + : id(operator_id), generator(std::move(create)), + m_expected_dimensions(std::move(expected_dimensions)) {} -Definition::Definition(Definition &&def) - : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} +Definition::Definition(Definition &&def) + : id(def.id), generator(std::move(def.generator)), + m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( const std::vector &relevant_dimensions, diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 2eab4ec238..5a3d2f8390 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -6,12 +6,12 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/operators.h" -#include #include "cudaq/helpers.h" #include "cudaq/cudm_error_handling.h" +#include "cudaq/operators.h" #include #include +#include #include namespace cudaq { @@ -22,8 +22,9 @@ class _OperatorHelpers { _OperatorHelpers() = default; // Aggregate parameters from multiple mappings. - std::map aggregate_parameters( - const std::vector> ¶meter_mappings) { + std::map + aggregate_parameters(const std::vector> + ¶meter_mappings) { std::map parameter_descriptions; for (const auto &descriptions : parameter_mappings) { @@ -41,17 +42,17 @@ class _OperatorHelpers { // Extract documentation for a specific parameter from docstring. std::string parameter_docs(const std::string ¶m_name, - const std::string &docs) { + const std::string &docs) { if (param_name.empty() || docs.empty()) { return ""; } try { std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); + std::regex::multiline); std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); std::smatch match; std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); @@ -73,10 +74,9 @@ class _OperatorHelpers { // Extract positional arguments and keyword-only arguments. std::pair, std::map> - args_from_kwargs( - const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { + args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { std::vector extracted_args; std::map kwonly_dict; @@ -99,8 +99,8 @@ class _OperatorHelpers { /// Generates all possible states for the given dimensions ordered according /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector - generate_all_states(std::vector degrees, std::map dimensions) { + std::vector generate_all_states(std::vector degrees, + std::map dimensions) { if (degrees.size() == 0) return {}; @@ -124,7 +124,7 @@ class _OperatorHelpers { } cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation) { + std::vector permutation) { auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); std::vector> sorted_values; for (std::size_t permuted : permutation) { @@ -146,7 +146,6 @@ class _OperatorHelpers { std::sort(degrees.begin(), degrees.end(), std::greater()); return degrees; } - }; -} -} +} // namespace detail +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h index 5f336847a5..ec5446143e 100644 --- a/runtime/cudaq/dynamics/helpers.h +++ b/runtime/cudaq/dynamics/helpers.h @@ -6,27 +6,27 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/utils/tensor.h" #include #include -#include "cudaq/utils/tensor.h" namespace cudaq { namespace detail { - /// Generates all possible states for the given dimensions ordered according - /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector generate_all_states(std::vector degrees, std::map dimensions); - - // Permutes the given matrix according to the given permutation. - // If states is the current order of vector entries on which the given matrix - // acts, and permuted_states is the desired order of an array on which the - // permuted matrix should act, then the permutation is defined such that - // [states[i] for i in permutation] produces permuted_states. - cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation); +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector generate_all_states(std::vector degrees, + std::map dimensions); - // Returns the degrees sorted in canonical order. - std::vector canonicalize_degrees(std::vector degrees); -} -} +// Permutes the given matrix according to the given permutation. +// If states is the current order of vector entries on which the given matrix +// acts, and permuted_states is the desired order of an array on which the +// permuted matrix should act, then the permutation is defined such that +// [states[i] for i in permutation] produces permuted_states. +cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation); +// Returns the degrees sorted in canonical order. +std::vector canonicalize_degrees(std::vector degrees); +} // namespace detail +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 5b713331a7..e3d6f90447 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -14,8 +14,7 @@ namespace cudaq { std::vector MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::vector canon_degrees) { - auto states = - cudaq::detail::generate_all_states(canon_degrees, m_dimensions); + auto states = cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; for (auto degree : op_degrees) { @@ -23,7 +22,7 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, reordering.push_back(it - canon_degrees.begin()); } - std::vector op_states = + std::vector op_states = cudaq::detail::generate_all_states(op_degrees, m_dimensions); std::vector permutation; @@ -99,8 +98,10 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix + op2.m_matrix)); } -EvaluatedMatrix MatrixArithmetics::evaluate( - std::variant> op) { +EvaluatedMatrix +MatrixArithmetics::evaluate(std::variant> + op) { // auto getDegrees = [](auto &&t) { return t.degrees; }; // auto toMatrix = [&](auto &&t) { // return t.to_matrix(this->m_dimensions, this->m_parameters); diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index b8926ba470..d0b460e1ea 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -8,8 +8,8 @@ #include "cudaq/operators.h" -#include #include +#include #include namespace cudaq { @@ -20,7 +20,7 @@ product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); @@ -40,7 +40,7 @@ product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when // the user invokes this later on via, e.g, `to_matrix()`. @@ -58,7 +58,7 @@ product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -76,7 +76,7 @@ product_operator matrix_operator::create(int degree) { std::string op_id = "create"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -94,7 +94,7 @@ product_operator matrix_operator::position(int degree) { std::string op_id = "position"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) @@ -116,7 +116,7 @@ product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) @@ -138,7 +138,7 @@ product_operator matrix_operator::number(int degree) { std::string op_id = "number"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -156,7 +156,7 @@ product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -174,7 +174,7 @@ product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> parameters) { + std::map> parameters) { std::size_t dimension = dimensions[0]; auto displacement_amplitude = parameters["displacement"]; auto create = matrix_2(dimension, dimension); @@ -194,12 +194,11 @@ product_operator matrix_operator::displace(int degree) { return product_operator(1., op); } - product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> parameters) { + std::map> parameters) { std::size_t dimension = dimensions[0]; auto squeezing = parameters["squeezing"]; auto create = matrix_2(dimension, dimension); @@ -220,17 +219,16 @@ product_operator matrix_operator::squeeze(int degree) { return product_operator(1., op); } - matrix_2 matrix_operator::to_matrix( std::map dimensions, std::map> parameters) const { auto it = matrix_operator::m_ops.find(this->id); if (it != matrix_operator::m_ops.end()) { - std::vector relevant_dimensions; - relevant_dimensions.reserve(this->degrees.size()); - for (auto d : this->degrees) - relevant_dimensions.push_back(dimensions[d]); - return it->second.generate_matrix(relevant_dimensions, parameters); + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->degrees.size()); + for (auto d : this->degrees) + relevant_dimensions.push_back(dimensions[d]); + return it->second.generate_matrix(relevant_dimensions, parameters); } throw std::range_error("unable to find operator"); } diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 858a4ec575..4f37bd536e 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -9,9 +9,9 @@ #include "cudaq/operators.h" #include "helpers.h" +#include #include #include -#include #include namespace cudaq { @@ -19,8 +19,9 @@ namespace cudaq { // private methods template -cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const { +cudaq::matrix_2 +operator_sum::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const { auto terms = this->get_terms(); auto degrees = this->degrees(); @@ -44,26 +45,29 @@ cudaq::matrix_2 operator_sum::m_evaluate( auto sum = EvaluatedMatrix(); if (pad_terms) { auto padded_term = paddedTerm(terms[0]); - sum = EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms)); + sum = EvaluatedMatrix(degrees, + padded_term.m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { padded_term = paddedTerm(terms[term_idx]); - sum = arithmetics.add(sum, EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms))); + sum = arithmetics.add( + sum, EvaluatedMatrix(degrees, + padded_term.m_evaluate(arithmetics, pad_terms))); } } else { - sum = - EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); + sum = EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; - auto eval = - term.m_evaluate(arithmetics, pad_terms); + auto eval = term.m_evaluate(arithmetics, pad_terms); sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); } } return sum.matrix(); } -template -std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const { +template +std::tuple, std::vector> +operator_sum::m_canonicalize_product( + product_operator &prod) const { std::vector scalars = {prod.get_coefficient()}; auto non_scalars = prod.get_terms(); @@ -109,8 +113,9 @@ std::tuple, std::vector> operator_sum -std::tuple, std::vector> operator_sum::m_canonical_terms() const { +template +std::tuple, std::vector> +operator_sum::m_canonical_terms() const { /// FIXME: Not doing the same sorting we do in python yet std::tuple, std::vector> result; std::vector scalars; @@ -120,195 +125,208 @@ std::tuple, std::vector> operator_sum(canon_term); auto canon_elementary = std::get<1>(canon_term); scalars.insert(scalars.end(), canon_scalars.begin(), canon_scalars.end()); - canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), canon_elementary.end()); + canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), + canon_elementary.end()); } return std::make_tuple(scalars, matrix_ops); } -template +template void operator_sum::aggregate_terms() {} -template -template -void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { - this->terms.push_back(head.terms[0]); - this->coefficients.push_back(head.coefficients[0]); - aggregate_terms(std::forward(args)...); +template +template +void operator_sum::aggregate_terms( + const product_operator &head, Args &&...args) { + this->terms.push_back(head.terms[0]); + this->coefficients.push_back(head.coefficients[0]); + aggregate_terms(std::forward(args)...); } -template -cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const; +template cudaq::matrix_2 +operator_sum::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const; -template -std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; +template std::tuple, std::vector> +operator_sum::m_canonicalize_product( + product_operator &prod) const; -template -std::tuple, std::vector> operator_sum::m_canonical_terms() const; +template std::tuple, std::vector> +operator_sum::m_canonical_terms() const; -// no overload for a single product, since we don't want a constructor for a single term +// no overload for a single product, since we don't want a constructor for a +// single term -template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2); +template void operator_sum::aggregate_terms( + const product_operator &item1, + const product_operator &item2); -template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +template void operator_sum::aggregate_terms( + const product_operator &item1, + const product_operator &item2, + const product_operator &item3); // read-only properties -template +template std::vector operator_sum::degrees() const { std::set unsorted_degrees; for (const std::vector &term : this->terms) { for (const HandlerTy &op : term) unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); } - auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + auto degrees = + std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); return cudaq::detail::canonicalize_degrees(degrees); } -template -int operator_sum::n_terms() const { - return this->terms.size(); +template +int operator_sum::n_terms() const { + return this->terms.size(); } -template -std::vector> operator_sum::get_terms() const { - std::vector> prods; - prods.reserve(this->terms.size()); - for (size_t i = 0; i < this->terms.size(); ++i) { - prods.push_back(product_operator(this->coefficients[i], this->terms[i])); - } - return prods; +template +std::vector> +operator_sum::get_terms() const { + std::vector> prods; + prods.reserve(this->terms.size()); + for (size_t i = 0; i < this->terms.size(); ++i) { + prods.push_back( + product_operator(this->coefficients[i], this->terms[i])); + } + return prods; } -template -std::vector operator_sum::degrees() const; +template std::vector operator_sum::degrees() const; -template -int operator_sum::n_terms() const; +template int operator_sum::n_terms() const; -template -std::vector> operator_sum::get_terms() const; +template std::vector> +operator_sum::get_terms() const; // constructors -template -template -operator_sum::operator_sum(const Args&... args) { - this->terms.reserve(sizeof...(Args)); - this->coefficients.reserve(sizeof...(Args)); - aggregate_terms(args...); +template +template +operator_sum::operator_sum(const Args &...args) { + this->terms.reserve(sizeof...(Args)); + this->coefficients.reserve(sizeof...(Args)); + aggregate_terms(args...); } -template -operator_sum::operator_sum(const std::vector> &terms) { - this->terms.reserve(terms.size()); - this->coefficients.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(term.terms[0]); - this->coefficients.push_back(term.coefficients[0]); - } +template +operator_sum::operator_sum( + const std::vector> &terms) { + this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); + for (const product_operator &term : terms) { + this->terms.push_back(term.terms[0]); + this->coefficients.push_back(term.coefficients[0]); + } } -template -operator_sum::operator_sum(std::vector> &&terms) { - this->terms.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(std::move(term.terms[0])); - this->coefficients.push_back(std::move(term.coefficients[0])); - } +template +operator_sum::operator_sum( + std::vector> &&terms) { + this->terms.reserve(terms.size()); + for (const product_operator &term : terms) { + this->terms.push_back(std::move(term.terms[0])); + this->coefficients.push_back(std::move(term.coefficients[0])); + } } -template +template operator_sum::operator_sum(const operator_sum &other) : coefficients(other.coefficients), terms(other.terms) {} -template -operator_sum::operator_sum(operator_sum &&other) - : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} +template +operator_sum::operator_sum(operator_sum &&other) + : coefficients(std::move(other.coefficients)), + terms(std::move(other.terms)) {} -// no constructor for a single product, since that one should remain a product op +// no constructor for a single product, since that one should remain a product +// op -template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2); +template operator_sum::operator_sum( + const product_operator &item1, + const product_operator &item2); -template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +template operator_sum::operator_sum( + const product_operator &item1, + const product_operator &item2, + const product_operator &item3); -template -operator_sum::operator_sum(const std::vector> &terms); +template operator_sum::operator_sum( + const std::vector> &terms); -template -operator_sum::operator_sum(std::vector> &&terms); +template operator_sum::operator_sum( + std::vector> &&terms); -template -operator_sum::operator_sum(const operator_sum &other); +template operator_sum::operator_sum( + const operator_sum &other); -template -operator_sum::operator_sum(operator_sum &&other); +template operator_sum::operator_sum( + operator_sum &&other); // assignments -template -operator_sum& operator_sum::operator=(const operator_sum &other) { - if (this != &other) { - coefficients = other.coefficients; - terms = other.terms; - } - return *this; +template +operator_sum & +operator_sum::operator=(const operator_sum &other) { + if (this != &other) { + coefficients = other.coefficients; + terms = other.terms; + } + return *this; } -template -operator_sum& operator_sum::operator=(operator_sum &&other) { - if (this != &other) { - coefficients = std::move(other.coefficients); - terms = std::move(other.terms); - } - return *this; +template +operator_sum & +operator_sum::operator=(operator_sum &&other) { + if (this != &other) { + coefficients = std::move(other.coefficients); + terms = std::move(other.terms); + } + return *this; } -template -operator_sum& operator_sum::operator=(const operator_sum& other); +template operator_sum & +operator_sum::operator=( + const operator_sum &other); -template -operator_sum& operator_sum::operator=(operator_sum &&other); +template operator_sum & +operator_sum::operator=(operator_sum &&other); // evaluations -template +template std::string operator_sum::to_string() const { - throw std::runtime_error("not implemented"); + throw std::runtime_error("not implemented"); } -template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map> ¶meters) const { +template +matrix_2 operator_sum::to_matrix( + const std::map &dimensions, + const std::map> ¶meters) const { return m_evaluate(MatrixArithmetics(dimensions, parameters)); } -template -std::string operator_sum::to_string() const; +template std::string operator_sum::to_string() const; -template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map> ¶ms) const; +template matrix_2 operator_sum::to_matrix( + const std::map &dimensions, + const std::map> ¶ms) const; // comparisons -template -bool operator_sum::operator==(const operator_sum &other) const { - throw std::runtime_error("not implemented"); +template +bool operator_sum::operator==( + const operator_sum &other) const { + throw std::runtime_error("not implemented"); } -template -bool operator_sum::operator==(const operator_sum &other) const; +template bool operator_sum::operator==( + const operator_sum &other) const; // unary operators @@ -329,48 +347,50 @@ operator_sum operator_sum::operator+() const { return *this; } -template -operator_sum operator_sum::operator-() const; +template operator_sum +operator_sum::operator-() const; -template -operator_sum operator_sum::operator+() const; +template operator_sum +operator_sum::operator+() const; // right-hand arithmetics -#define SUM_MULTIPLICATION(otherTy) \ - template \ - operator_sum operator_sum::operator*(otherTy other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size()); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff * other); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = this->terms; \ - return sum; \ +#define SUM_MULTIPLICATION(otherTy) \ + template \ + operator_sum operator_sum::operator*(otherTy other) \ + const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size()); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff *other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = this->terms; \ + return sum; \ } SUM_MULTIPLICATION(double); SUM_MULTIPLICATION(std::complex); SUM_MULTIPLICATION(const scalar_operator &); -#define SUM_ADDITION(otherTy, op) \ - template \ - operator_sum operator_sum::operator op(otherTy other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - coefficients.push_back(op other); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - terms.push_back({}); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION(otherTy, op) \ + template \ + operator_sum operator_sum::operator op(otherTy other) \ + const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op other); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + terms.push_back({}); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION(double, +); @@ -381,7 +401,8 @@ SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); template -operator_sum operator_sum::operator*(const HandlerTy &other) const { +operator_sum +operator_sum::operator*(const HandlerTy &other) const { std::vector> terms; terms.reserve(this->terms.size()); for (auto &term : this->terms) { @@ -398,58 +419,59 @@ operator_sum operator_sum::operator*(const HandlerTy &othe return sum; } -#define SUM_ADDITION_HANDLER(op) \ - template \ - operator_sum operator_sum::operator op( \ - const HandlerTy &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - coefficients.push_back(op 1.); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - terms.push_back(std::move(newTerm)); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_HANDLER(op) \ + template \ + operator_sum operator_sum::operator op( \ + const HandlerTy &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op 1.); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_HANDLER(+) SUM_ADDITION_HANDLER(-) -template -operator_sum operator_sum::operator*(double other) const; -template -operator_sum operator_sum::operator+(double other) const; -template -operator_sum operator_sum::operator-(double other) const; -template -operator_sum operator_sum::operator*(std::complex other) const; -template -operator_sum operator_sum::operator+(std::complex other) const; -template -operator_sum operator_sum::operator-(std::complex other) const; -template -operator_sum operator_sum::operator*(const scalar_operator &other) const; -template -operator_sum operator_sum::operator+(const scalar_operator &other) const; -template -operator_sum operator_sum::operator-(const scalar_operator &other) const; -template -operator_sum operator_sum::operator*(const matrix_operator &other) const; -template -operator_sum operator_sum::operator+(const matrix_operator &other) const; -template -operator_sum operator_sum::operator-(const matrix_operator &other) const; +template operator_sum +operator_sum::operator*(double other) const; +template operator_sum +operator_sum::operator+(double other) const; +template operator_sum +operator_sum::operator-(double other) const; +template operator_sum +operator_sum::operator*(std::complex other) const; +template operator_sum +operator_sum::operator+(std::complex other) const; +template operator_sum +operator_sum::operator-(std::complex other) const; +template operator_sum +operator_sum::operator*(const scalar_operator &other) const; +template operator_sum +operator_sum::operator+(const scalar_operator &other) const; +template operator_sum +operator_sum::operator-(const scalar_operator &other) const; +template operator_sum +operator_sum::operator*(const matrix_operator &other) const; +template operator_sum +operator_sum::operator+(const matrix_operator &other) const; +template operator_sum +operator_sum::operator-(const matrix_operator &other) const; template -operator_sum operator_sum::operator*(const product_operator &other) const { +operator_sum operator_sum::operator*( + const product_operator &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size()); for (auto &coeff : this->coefficients) @@ -459,7 +481,7 @@ operator_sum operator_sum::operator*(const product_operato for (auto &term : this->terms) { std::vector prod; prod.reserve(term.size() + other.terms[0].size()); - for (auto &op : term) + for (auto &op : term) prod.push_back(op); for (auto &op : other.terms[0]) prod.push_back(op); @@ -471,31 +493,32 @@ operator_sum operator_sum::operator*(const product_operato return sum; } -#define SUM_ADDITION_PRODUCT(op) \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - coefficients.push_back(op other.coefficients[0]); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - terms.push_back(other.terms[0]); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_PRODUCT(op) \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + coefficients.push_back(op other.coefficients[0]); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + terms.push_back(other.terms[0]); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum operator_sum::operator*(const operator_sum &other) const { +operator_sum +operator_sum::operator*(const operator_sum &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size() * other.coefficients.size()); for (auto &coeff1 : this->coefficients) { @@ -521,62 +544,65 @@ operator_sum operator_sum::operator*(const operator_sum \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_SUM(op) \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + \ + other.coefficients.size()); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + for (auto &coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + for (auto &term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); -template -operator_sum operator_sum::operator*(const product_operator &other) const; -template -operator_sum operator_sum::operator+(const product_operator &other) const; -template -operator_sum operator_sum::operator-(const product_operator &other) const; -template -operator_sum operator_sum::operator*(const operator_sum &other) const; -template -operator_sum operator_sum::operator+(const operator_sum &other) const; -template -operator_sum operator_sum::operator-(const operator_sum &other) const; - -#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - operator_sum& operator_sum::operator*=(otherTy other) { \ - for (auto &coeff : this->coefficients) \ - coeff *= other; \ - return *this; \ +template operator_sum operator_sum::operator*( + const product_operator &other) const; +template operator_sum operator_sum::operator+( + const product_operator &other) const; +template operator_sum operator_sum::operator-( + const product_operator &other) const; +template operator_sum operator_sum::operator*( + const operator_sum &other) const; +template operator_sum operator_sum::operator+( + const operator_sum &other) const; +template operator_sum operator_sum::operator-( + const operator_sum &other) const; + +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum &operator_sum::operator*=( \ + otherTy other) { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return *this; \ } SUM_MULTIPLICATION_ASSIGNMENT(double); SUM_MULTIPLICATION_ASSIGNMENT(std::complex); SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ - template \ - operator_sum& operator_sum::operator op##=(otherTy other) { \ - this->coefficients.push_back(op other); \ - this->terms.push_back({}); \ - return *this; \ +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + otherTy other) { \ + this->coefficients.push_back(op other); \ + this->terms.push_back({}); \ + return *this; \ } SUM_ADDITION_ASSIGNMENT(double, +); @@ -587,29 +613,31 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template -operator_sum& operator_sum::operator*=(const HandlerTy &other) { +operator_sum & +operator_sum::operator*=(const HandlerTy &other) { for (auto &term : this->terms) term.push_back(other); operator_sum sum; return *this; } -#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const HandlerTy &other) { \ - coefficients.push_back(op 1.); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - this->terms.push_back(std::move(newTerm)); \ - return *this; \ +#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const HandlerTy &other) { \ + coefficients.push_back(op 1.); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + this->terms.push_back(std::move(newTerm)); \ + return *this; \ } SUM_ADDITION_HANDLER_ASSIGNMENT(+) SUM_ADDITION_HANDLER_ASSIGNMENT(-) template -operator_sum& operator_sum::operator*=(const product_operator &other) { +operator_sum & +operator_sum::operator*=(const product_operator &other) { for (auto &coeff : this->coefficients) coeff *= other.coefficients[0]; for (auto &term : this->terms) { @@ -620,116 +648,125 @@ operator_sum& operator_sum::operator*=(const product_opera return *this; } -#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const product_operator &other) { \ - this->coefficients.push_back(op other.coefficients[0]); \ - this->terms.push_back(other.terms[0]); \ - return *this; \ +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const product_operator &other) { \ + this->coefficients.push_back(op other.coefficients[0]); \ + this->terms.push_back(other.terms[0]); \ + return *this; \ } SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template -operator_sum& operator_sum::operator*=(const operator_sum &other) { - this->coefficients.reserve(this->coefficients.size() * other.coefficients.size()); +operator_sum & +operator_sum::operator*=(const operator_sum &other) { + this->coefficients.reserve(this->coefficients.size() * + other.coefficients.size()); *this = *this * other; // we need to update all coefficients and terms anyway return *this; } -#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const operator_sum &other) { \ - this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - for (auto &coeff : other.coefficients) \ - this->coefficients.push_back(op coeff); \ - this->terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &term : other.terms) \ - this->terms.push_back(term); \ - return *this; \ +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const operator_sum &other) { \ + this->coefficients.reserve(this->coefficients.size() + \ + other.coefficients.size()); \ + for (auto &coeff : other.coefficients) \ + this->coefficients.push_back(op coeff); \ + this->terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : other.terms) \ + this->terms.push_back(term); \ + return *this; \ } SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); -template -operator_sum& operator_sum::operator*=(double other); -template -operator_sum& operator_sum::operator+=(double other); -template -operator_sum& operator_sum::operator-=(double other); -template -operator_sum& operator_sum::operator*=(std::complex other); -template -operator_sum& operator_sum::operator+=(std::complex other); -template -operator_sum& operator_sum::operator-=(std::complex other); -template -operator_sum& operator_sum::operator*=(const scalar_operator &other); -template -operator_sum& operator_sum::operator+=(const scalar_operator &other); -template -operator_sum& operator_sum::operator-=(const scalar_operator &other); -template -operator_sum& operator_sum::operator*=(const matrix_operator &other); -template -operator_sum& operator_sum::operator+=(const matrix_operator &other); -template -operator_sum& operator_sum::operator-=(const matrix_operator &other); -template -operator_sum& operator_sum::operator*=(const product_operator &other); -template -operator_sum& operator_sum::operator+=(const product_operator &other); -template -operator_sum& operator_sum::operator-=(const product_operator &other); -template -operator_sum& operator_sum::operator*=(const operator_sum &other); -template -operator_sum& operator_sum::operator-=(const operator_sum &other); -template -operator_sum& operator_sum::operator+=(const operator_sum &other); +template operator_sum & +operator_sum::operator*=(double other); +template operator_sum & +operator_sum::operator+=(double other); +template operator_sum & +operator_sum::operator-=(double other); +template operator_sum & +operator_sum::operator*=(std::complex other); +template operator_sum & +operator_sum::operator+=(std::complex other); +template operator_sum & +operator_sum::operator-=(std::complex other); +template operator_sum & +operator_sum::operator*=(const scalar_operator &other); +template operator_sum & +operator_sum::operator+=(const scalar_operator &other); +template operator_sum & +operator_sum::operator-=(const scalar_operator &other); +template operator_sum & +operator_sum::operator*=(const matrix_operator &other); +template operator_sum & +operator_sum::operator+=(const matrix_operator &other); +template operator_sum & +operator_sum::operator-=(const matrix_operator &other); +template operator_sum & +operator_sum::operator*=( + const product_operator &other); +template operator_sum & +operator_sum::operator+=( + const product_operator &other); +template operator_sum & +operator_sum::operator-=( + const product_operator &other); +template operator_sum & +operator_sum::operator*=( + const operator_sum &other); +template operator_sum & +operator_sum::operator-=( + const operator_sum &other); +template operator_sum & +operator_sum::operator+=( + const operator_sum &other); // left-hand arithmetics -#define SUM_MULTIPLICATION_REVERSE(otherTy) \ - template \ - operator_sum operator*(otherTy other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.coefficients.size()); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(coeff * other); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = self.terms; \ - return sum; \ +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.coefficients.size()); \ + for (auto &coeff : self.coefficients) \ + coefficients.push_back(coeff *other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = self.terms; \ + return sum; \ } SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); -#define SUM_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.terms.size() + 1); \ - coefficients.push_back(other); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(self.terms.size() + 1); \ - terms.push_back({}); \ - for (auto &term : self.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(other); \ + for (auto &coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + terms.push_back({}); \ + for (auto &term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_REVERSE(double, +); @@ -740,7 +777,8 @@ SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { +operator_sum operator*(const HandlerTy &other, + const operator_sum &self) { std::vector> terms; terms.reserve(self.terms.size()); for (auto &term : self.terms) { @@ -757,54 +795,63 @@ operator_sum operator*(const HandlerTy &other, const operator_sum \ - operator_sum operator op(const HandlerTy &other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.terms.size() + 1); \ - coefficients.push_back(1.); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(self.terms.size() + 1); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - terms.push_back(std::move(newTerm)); \ - for (auto &term : self.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy &other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(1.); \ + for (auto &coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto &term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_HANDLER_REVERSE(+) SUM_ADDITION_HANDLER_REVERSE(-) -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator*(const matrix_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator+(const matrix_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator-(const matrix_operator &other, const operator_sum &self); +template operator_sum +operator*(const scalar_operator &other, + const operator_sum &self); +template operator_sum +operator*(std::complex other, + const operator_sum &self); +template operator_sum +operator*(double other, const operator_sum &self); +template operator_sum +operator*(const matrix_operator &other, + const operator_sum &self); +template operator_sum +operator+(const scalar_operator &other, + const operator_sum &self); +template operator_sum +operator+(double other, const operator_sum &self); +template operator_sum +operator+(std::complex other, + const operator_sum &self); +template operator_sum +operator+(const matrix_operator &other, + const operator_sum &self); +template operator_sum +operator-(const scalar_operator &other, + const operator_sum &self); +template operator_sum +operator-(double other, const operator_sum &self); +template operator_sum +operator-(std::complex other, + const operator_sum &self); +template operator_sum +operator-(const matrix_operator &other, + const operator_sum &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 0489eb9a4c..80ea64f6df 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace cudaq { @@ -26,37 +25,48 @@ _padded_op(MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, /// Creating the tensor product with op being last is most efficient. std::vector padded; for (const auto °ree : degrees) { - if (std::find(op.degrees.begin(), op.degrees.end(), degree) == op.degrees.end()) { - // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS ARE REQUIRED. - padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); - // FIXME: avoid creation of a product here - - // but we need to make sure identity is defined before using it (all ops are lazily defined...) - //padded.push_back(cudaq::matrix_operator("identity", {degree}).to_matrix()); + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == + op.degrees.end()) { + // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS + // ARE REQUIRED. + padded.push_back(EvaluatedMatrix( + {degree}, matrix_operator::identity(degree).to_matrix(dimensions))); + // FIXME: avoid creation of a product here - + // but we need to make sure identity is defined before using it (all ops + // are lazily defined...) + // padded.push_back(cudaq::matrix_operator("identity", + // {degree}).to_matrix()); } } matrix_2 mat = op.to_matrix(dimensions, parameters); auto res = EvaluatedMatrix(op.degrees, mat); // FIXME: PUT THIS LAST - for(auto &op : padded) + for (auto &op : padded) res = arithmetics.tensor(res, op); - return res; + return res; } -// FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - +// FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - // IT SUPPOSED TO TAKE A TRANSFORMATION (ANY OPERATOR ARITHMETICS) AND APPLY IT template -cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const { +cudaq::matrix_2 +product_operator::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const { const std::vector &terms = this->terms[0]; auto degrees = this->degrees(); cudaq::matrix_2 result; if (terms.size() > 0) { if (pad_terms) { - auto evaluated = _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + auto evaluated = + _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, + arithmetics.m_parameters); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { const HandlerTy &op = terms[op_idx]; - if (op.degrees.size() != 1 || op != cudaq::matrix_operator("identity", op.degrees)) { - auto padded = _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + if (op.degrees.size() != 1 || + op != cudaq::matrix_operator("identity", op.degrees)) { + auto padded = + _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, + arithmetics.m_parameters); evaluated = arithmetics.mul(evaluated, padded); } } @@ -65,8 +75,10 @@ cudaq::matrix_2 product_operator::m_evaluate( auto evaluated = arithmetics.evaluate(terms[0]); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { auto &op = terms[op_idx]; - auto mat = op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); - evaluated = arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + auto mat = + op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = + arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); } result = evaluated.matrix(); } @@ -79,73 +91,72 @@ cudaq::matrix_2 product_operator::m_evaluate( return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } -template +template void product_operator::aggregate_terms() {} -template -template -void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { +template +template +void product_operator::aggregate_terms(const HandlerTy &head, + Args &&...args) { this->terms[0].push_back(head); aggregate_terms(std::forward(args)...); } -template -void product_operator::aggregate_terms(const matrix_operator &item1, - const matrix_operator &item2); +template void product_operator::aggregate_terms( + const matrix_operator &item1, const matrix_operator &item2); -template -void product_operator::aggregate_terms(const matrix_operator &item1, - const matrix_operator &item2, - const matrix_operator &item3); +template void product_operator::aggregate_terms( + const matrix_operator &item1, const matrix_operator &item2, + const matrix_operator &item3); // read-only properties -template +template std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->terms[0]) { unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); } - auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + auto degrees = + std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); return cudaq::detail::canonicalize_degrees(degrees); } -template -int product_operator::n_terms() const { - return this->terms[0].size(); +template +int product_operator::n_terms() const { + return this->terms[0].size(); } -template -std::vector product_operator::get_terms() const { - return this->terms[0]; +template +std::vector product_operator::get_terms() const { + return this->terms[0]; } -template -scalar_operator product_operator::get_coefficient() const { - return this->coefficients[0]; +template +scalar_operator product_operator::get_coefficient() const { + return this->coefficients[0]; } -template -cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const; +template cudaq::matrix_2 +product_operator::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const; -template -std::vector product_operator::degrees() const; +template std::vector product_operator::degrees() const; -template -int product_operator::n_terms() const; +template int product_operator::n_terms() const; -template -std::vector product_operator::get_terms() const; +template std::vector +product_operator::get_terms() const; -template -scalar_operator product_operator::get_coefficient() const; +template scalar_operator +product_operator::get_coefficient() const; // constructors -template -template -product_operator::product_operator(scalar_operator coefficient, const Args&... args) { +template +template +product_operator::product_operator(scalar_operator coefficient, + const Args &...args) { this->coefficients.push_back(std::move(coefficient)); std::vector ops = {}; ops.reserve(sizeof...(Args)); @@ -153,64 +164,68 @@ product_operator::product_operator(scalar_operator coefficient, const aggregate_terms(args...); } -template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) { +template +product_operator::product_operator( + scalar_operator coefficient, + const std::vector &atomic_operators) { this->terms.push_back(atomic_operators); this->coefficients.push_back(std::move(coefficient)); } -template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) { +template +product_operator::product_operator( + scalar_operator coefficient, std::vector &&atomic_operators) { this->terms.push_back(std::move(atomic_operators)); this->coefficients.push_back(std::move(coefficient)); } -template -product_operator::product_operator(const product_operator &other) { +template +product_operator::product_operator( + const product_operator &other) { this->terms = other.terms; this->coefficients = other.coefficients; } -template -product_operator::product_operator(product_operator &&other) { +template +product_operator::product_operator( + product_operator &&other) { this->terms = std::move(other.terms); this->coefficients = std::move(other.coefficients); } -template -product_operator::product_operator(scalar_operator coefficient); +template product_operator::product_operator( + scalar_operator coefficient); -template -product_operator::product_operator(scalar_operator coefficient, - const matrix_operator &item1); +template product_operator::product_operator( + scalar_operator coefficient, const matrix_operator &item1); -template -product_operator::product_operator(scalar_operator coefficient, - const matrix_operator &item1, - const matrix_operator &item2); +template product_operator::product_operator( + scalar_operator coefficient, const matrix_operator &item1, + const matrix_operator &item2); -template -product_operator::product_operator(scalar_operator coefficient, - const matrix_operator &item1, - const matrix_operator &item2, - const matrix_operator &item3); +template product_operator::product_operator( + scalar_operator coefficient, const matrix_operator &item1, + const matrix_operator &item2, const matrix_operator &item3); -template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); +template product_operator::product_operator( + scalar_operator coefficient, + const std::vector &atomic_operators); -template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); +template product_operator::product_operator( + scalar_operator coefficient, + std::vector &&atomic_operators); -template -product_operator::product_operator(const product_operator &other); +template product_operator::product_operator( + const product_operator &other); -template -product_operator::product_operator(product_operator &&other); +template product_operator::product_operator( + product_operator &&other); // assignments -template -product_operator& product_operator::operator=(const product_operator &other) { +template +product_operator &product_operator::operator=( + const product_operator &other) { if (this != &other) { this->terms = other.terms; this->coefficients = other.coefficients; @@ -218,8 +233,9 @@ product_operator& product_operator::operator=(const produc return *this; } -template -product_operator& product_operator::operator=(product_operator &&other) { +template +product_operator & +product_operator::operator=(product_operator &&other) { if (this != &other) { this->coefficients = std::move(other.coefficients); this->terms = std::move(other.terms); @@ -227,47 +243,51 @@ product_operator& product_operator::operator=(product_oper return *this; } -template -product_operator& product_operator::operator=(const product_operator &other); +template product_operator & +product_operator::operator=( + const product_operator &other); -template -product_operator& product_operator::operator=(product_operator &&other); +template product_operator & +product_operator::operator=( + product_operator &&other); // evaluations -template +template std::string product_operator::to_string() const { throw std::runtime_error("not implemented"); } -template -matrix_2 product_operator::to_matrix(std::map dimensions, - std::map> parameters) const { +template +matrix_2 product_operator::to_matrix( + std::map dimensions, + std::map> parameters) const { return this->m_evaluate(MatrixArithmetics(dimensions, parameters)); } -template -std::string product_operator::to_string() const; +template std::string product_operator::to_string() const; -template -matrix_2 product_operator::to_matrix(std::map dimensions, - std::map> parameters) const; +template matrix_2 product_operator::to_matrix( + std::map dimensions, + std::map> parameters) const; // comparisons -template -bool product_operator::operator==(const product_operator &other) const { +template +bool product_operator::operator==( + const product_operator &other) const { throw std::runtime_error("not implemented"); } -template -bool product_operator::operator==(const product_operator &other) const; +template bool product_operator::operator==( + const product_operator &other) const; // unary operators template product_operator product_operator::operator-() const { - return product_operator(-1. * this->coefficients[0], this->terms[0]); + return product_operator(-1. * this->coefficients[0], + this->terms[0]); } template @@ -275,30 +295,32 @@ product_operator product_operator::operator+() const { return *this; } -template -product_operator product_operator::operator-() const; +template product_operator +product_operator::operator-() const; -template -product_operator product_operator::operator+() const; +template product_operator +product_operator::operator+() const; // right-hand arithmetics -#define PRODUCT_MULTIPLICATION(otherTy) \ - template \ - product_operator product_operator::operator*( \ - otherTy other) const { \ - return product_operator(other * this->coefficients[0], this->terms[0]); \ +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficients[0], \ + this->terms[0]); \ } PRODUCT_MULTIPLICATION(double); PRODUCT_MULTIPLICATION(std::complex); PRODUCT_MULTIPLICATION(const scalar_operator &); -#define PRODUCT_ADDITION(otherTy, op) \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) const { \ - return operator_sum(product_operator(op other), *this); \ +#define PRODUCT_ADDITION(otherTy, op) \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const { \ + return operator_sum(product_operator(op other), \ + *this); \ } PRODUCT_ADDITION(double, +); @@ -309,7 +331,8 @@ PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); template -product_operator product_operator::operator*(const HandlerTy &other) const { +product_operator +product_operator::operator*(const HandlerTy &other) const { std::vector terms; terms.reserve(this->terms[0].size() + 1); for (auto &term : this->terms[0]) @@ -318,64 +341,74 @@ product_operator product_operator::operator*(const Handler return product_operator(this->coefficients[0], std::move(terms)); } -#define PRODUCT_ADDITION_HANDLER(op) \ - template \ - operator_sum product_operator::operator op( \ - const HandlerTy &other) const { \ - return operator_sum(product_operator(op 1., other), *this); \ +#define PRODUCT_ADDITION_HANDLER(op) \ + template \ + operator_sum product_operator::operator op( \ + const HandlerTy &other) const { \ + return operator_sum(product_operator(op 1., other), \ + *this); \ } PRODUCT_ADDITION_HANDLER(+) PRODUCT_ADDITION_HANDLER(-) -template -product_operator product_operator::operator*(double other) const; -template -operator_sum product_operator::operator+(double other) const; -template -operator_sum product_operator::operator-(double other) const; -template -product_operator product_operator::operator*(std::complex other) const; -template -operator_sum product_operator::operator+(std::complex other) const; -template -operator_sum product_operator::operator-(std::complex other) const; -template -product_operator product_operator::operator*(const scalar_operator &other) const; -template -operator_sum product_operator::operator+(const scalar_operator &other) const; -template -operator_sum product_operator::operator-(const scalar_operator &other) const; -template -product_operator product_operator::operator*(const matrix_operator &other) const; -template -operator_sum product_operator::operator+(const matrix_operator &other) const; -template -operator_sum product_operator::operator-(const matrix_operator &other) const; +template product_operator +product_operator::operator*(double other) const; +template operator_sum +product_operator::operator+(double other) const; +template operator_sum +product_operator::operator-(double other) const; +template product_operator +product_operator::operator*(std::complex other) const; +template operator_sum +product_operator::operator+(std::complex other) const; +template operator_sum +product_operator::operator-(std::complex other) const; +template product_operator +product_operator::operator*( + const scalar_operator &other) const; +template operator_sum +product_operator::operator+( + const scalar_operator &other) const; +template operator_sum +product_operator::operator-( + const scalar_operator &other) const; +template product_operator +product_operator::operator*( + const matrix_operator &other) const; +template operator_sum +product_operator::operator+( + const matrix_operator &other) const; +template operator_sum +product_operator::operator-( + const matrix_operator &other) const; template -product_operator product_operator::operator*(const product_operator &other) const { +product_operator product_operator::operator*( + const product_operator &other) const { std::vector terms; terms.reserve(this->terms[0].size() + other.terms[0].size()); for (auto &term : this->terms[0]) terms.push_back(term); for (auto &term : other.terms[0]) terms.push_back(term); - return product_operator(this->coefficients[0] * other.coefficients[0], std::move(terms)); + return product_operator(this->coefficients[0] * other.coefficients[0], + std::move(terms)); } -#define PRODUCT_ADDITION_PRODUCT(op) \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) const { \ - return operator_sum(op other, *this); \ +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(op other, *this); \ } PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) template -operator_sum product_operator::operator*(const operator_sum &other) const { +operator_sum product_operator::operator*( + const operator_sum &other) const { std::vector coefficients; coefficients.reserve(other.coefficients.size()); for (auto &coeff : other.coefficients) @@ -387,7 +420,7 @@ operator_sum product_operator::operator*(const operator_su prod.reserve(this->terms[0].size() + term.size()); for (auto &op : this->terms[0]) prod.push_back(op); - for (auto &op : term) + for (auto &op : term) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -397,47 +430,54 @@ operator_sum product_operator::operator*(const operator_su return sum; } -#define PRODUCT_ADDITION_SUM(op) \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(other.coefficients.size() + 1); \ - coefficients.push_back(this->coefficients[0]); \ - for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(other.terms.size() + 1); \ - terms.push_back(this->terms[0]); \ - for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(other.coefficients.size() + 1); \ + coefficients.push_back(this->coefficients[0]); \ + for (auto &coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(other.terms.size() + 1); \ + terms.push_back(this->terms[0]); \ + for (auto &term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) -template -product_operator product_operator::operator*(const product_operator &other) const; -template -operator_sum product_operator::operator+(const product_operator &other) const; -template -operator_sum product_operator::operator-(const product_operator &other) const; -template -operator_sum product_operator::operator*(const operator_sum &other) const; -template -operator_sum product_operator::operator+(const operator_sum &other) const; -template -operator_sum product_operator::operator-(const operator_sum &other) const; - -#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - product_operator& product_operator::operator*=(otherTy other) { \ - this->coefficients[0] *= other; \ - return *this; \ +template product_operator +product_operator::operator*( + const product_operator &other) const; +template operator_sum +product_operator::operator+( + const product_operator &other) const; +template operator_sum +product_operator::operator-( + const product_operator &other) const; +template operator_sum +product_operator::operator*( + const operator_sum &other) const; +template operator_sum +product_operator::operator+( + const operator_sum &other) const; +template operator_sum +product_operator::operator-( + const operator_sum &other) const; + +#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + product_operator &product_operator::operator*=( \ + otherTy other) { \ + this->coefficients[0] *= other; \ + return *this; \ } PRODUCT_MULTIPLICATION_ASSIGNMENT(double); @@ -445,48 +485,54 @@ PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); template -product_operator& product_operator::operator*=(const HandlerTy &other) { +product_operator & +product_operator::operator*=(const HandlerTy &other) { this->terms[0].push_back(other); return *this; } template -product_operator& product_operator::operator*=(const product_operator &other) { +product_operator &product_operator::operator*=( + const product_operator &other) { this->coefficients[0] *= other.coefficients[0]; this->terms[0].reserve(this->terms[0].size() + other.terms[0].size()); - this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), other.terms[0].end()); + this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), + other.terms[0].end()); return *this; } -template -product_operator& product_operator::operator*=(double other); -template -product_operator& product_operator::operator*=(std::complex other); -template -product_operator& product_operator::operator*=(const scalar_operator &other); -template -product_operator& product_operator::operator*=(const matrix_operator &other); -template -product_operator& product_operator::operator*=(const product_operator &other); +template product_operator & +product_operator::operator*=(double other); +template product_operator & +product_operator::operator*=(std::complex other); +template product_operator & +product_operator::operator*=(const scalar_operator &other); +template product_operator & +product_operator::operator*=(const matrix_operator &other); +template product_operator & +product_operator::operator*=( + const product_operator &other); // left-hand arithmetics -#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ - template \ - product_operator operator*(otherTy other, \ - const product_operator &self) { \ - return product_operator(other * self.coefficients[0], self.terms[0]); \ +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*( \ + otherTy other, const product_operator &self) { \ + return product_operator(other * self.coefficients[0], \ + self.terms[0]); \ } PRODUCT_MULTIPLICATION_REVERSE(double); PRODUCT_MULTIPLICATION_REVERSE(std::complex); PRODUCT_MULTIPLICATION_REVERSE(const scalar_operator &); -#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const product_operator &self) { \ - return operator_sum(product_operator(other), op self); \ +#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op( \ + otherTy other, const product_operator &self) { \ + return operator_sum(product_operator(other), \ + op self); \ } PRODUCT_ADDITION_REVERSE(double, +); @@ -497,7 +543,8 @@ PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); template -product_operator operator*(const HandlerTy &other, const product_operator &self) { +product_operator operator*(const HandlerTy &other, + const product_operator &self) { std::vector terms; terms.reserve(self.terms[0].size() + 1); terms.push_back(other); @@ -506,39 +553,49 @@ product_operator operator*(const HandlerTy &other, const product_oper return product_operator(self.coefficients[0], std::move(terms)); } -#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ - template \ - operator_sum operator op(const HandlerTy &other, \ - const product_operator &self) { \ - return operator_sum(product_operator(1., other), op self); \ +#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op( \ + const HandlerTy &other, const product_operator &self) { \ + return operator_sum(product_operator(1., other), \ + op self); \ } PRODUCT_ADDITION_HANDLER_REVERSE(+) PRODUCT_ADDITION_HANDLER_REVERSE(-) -template -product_operator operator*(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(const matrix_operator &other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(const matrix_operator &other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(const matrix_operator &other, const product_operator &self); +template product_operator +operator*(double other, const product_operator &self); +template product_operator +operator*(std::complex other, + const product_operator &self); +template product_operator +operator*(const scalar_operator &other, + const product_operator &self); +template product_operator +operator*(const matrix_operator &other, + const product_operator &self); +template operator_sum +operator+(double other, const product_operator &self); +template operator_sum +operator+(std::complex other, + const product_operator &self); +template operator_sum +operator+(const scalar_operator &other, + const product_operator &self); +template operator_sum +operator+(const matrix_operator &other, + const product_operator &self); +template operator_sum +operator-(double other, const product_operator &self); +template operator_sum +operator-(std::complex other, + const product_operator &self); +template operator_sum +operator-(const scalar_operator &other, + const product_operator &self); +template operator_sum +operator-(const matrix_operator &other, + const product_operator &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index b933ad0783..5371926a07 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -15,31 +15,31 @@ namespace cudaq { // constructors and destructors -scalar_operator::scalar_operator(double value) - : constant_value(value), generator() {} +scalar_operator::scalar_operator(double value) + : constant_value(value), generator() {} -scalar_operator::scalar_operator(std::complex value) - : constant_value(value), generator() {} +scalar_operator::scalar_operator(std::complex value) + : constant_value(value), generator() {} -scalar_operator::scalar_operator(const ScalarCallbackFunction &create) - : constant_value(), generator(create) {} +scalar_operator::scalar_operator(const ScalarCallbackFunction &create) + : constant_value(), generator(create) {} scalar_operator::scalar_operator(ScalarCallbackFunction &&create) - : constant_value() { - generator = std::move(create); + : constant_value() { + generator = std::move(create); } -scalar_operator::scalar_operator(const scalar_operator &other) - : constant_value(other.constant_value), generator(other.generator) {} +scalar_operator::scalar_operator(const scalar_operator &other) + : constant_value(other.constant_value), generator(other.generator) {} -scalar_operator::scalar_operator(scalar_operator &&other) - : constant_value(other.constant_value) { - generator = std::move(other.generator); +scalar_operator::scalar_operator(scalar_operator &&other) + : constant_value(other.constant_value) { + generator = std::move(other.generator); } // assignments -scalar_operator& scalar_operator::operator=(const scalar_operator &other) { +scalar_operator &scalar_operator::operator=(const scalar_operator &other) { if (this != &other) { constant_value = other.constant_value; generator = other.generator; @@ -47,7 +47,7 @@ scalar_operator& scalar_operator::operator=(const scalar_operator &other) { return *this; } -scalar_operator& scalar_operator::operator=(scalar_operator &&other) { +scalar_operator &scalar_operator::operator=(scalar_operator &&other) { if (this != &other) { constant_value = other.constant_value; generator = std::move(other.generator); @@ -59,8 +59,10 @@ scalar_operator& scalar_operator::operator=(scalar_operator &&other) { std::complex scalar_operator::evaluate( const std::map> parameters) const { - if (constant_value.has_value()) return constant_value.value(); - else return generator(parameters); + if (constant_value.has_value()) + return constant_value.value(); + else + return generator(parameters); } matrix_2 scalar_operator::to_matrix( @@ -83,13 +85,9 @@ bool scalar_operator::operator==(scalar_operator other) { // unary operators -scalar_operator scalar_operator::operator-() const { - return *this * (-1.); -} +scalar_operator scalar_operator::operator-() const { return *this * (-1.); } -scalar_operator scalar_operator::operator+() const { - return *this; -} +scalar_operator scalar_operator::operator+() const { return *this; } // right-hand arithmetics @@ -99,10 +97,10 @@ scalar_operator scalar_operator::operator+() const { return scalar_operator(this->constant_value.value() op other); \ } \ auto newGenerator = \ - [other, generator = this->generator]( \ - std::map> parameters) { \ - return generator(parameters) op other; \ - }; \ + [other, generator = this->generator]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ return scalar_operator(newGenerator); \ } @@ -116,16 +114,16 @@ ARITHMETIC_OPERATIONS(+, std::complex); ARITHMETIC_OPERATIONS(-, std::complex); #define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op( \ - const scalar_operator &other) const { \ + scalar_operator scalar_operator::operator op(const scalar_operator &other) \ + const { \ if (this->constant_value.has_value() && \ other.constant_value.has_value()) { \ auto res = this->constant_value.value() op other.constant_value.value(); \ return scalar_operator(res); \ } \ auto newGenerator = \ - [other, *this]( \ - std::map> parameters) { \ + [other, \ + *this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(newGenerator); \ @@ -137,16 +135,16 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); #define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ - scalar_operator& scalar_operator::operator op(otherTy other) { \ + scalar_operator &scalar_operator::operator op(otherTy other) { \ if (this->constant_value.has_value()) { \ this->constant_value.value() op other; \ return *this; \ } \ auto newGenerator = \ - [other, generator = std::move(this->generator)]( \ - std::map> parameters) { \ - return generator(parameters) op other; \ - }; \ + [other, generator = std::move(this->generator)]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ this->generator = newGenerator; \ return *this; \ } @@ -161,16 +159,16 @@ ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, std::complex); ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, std::complex); #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op( \ - const scalar_operator &other) { \ + scalar_operator &scalar_operator::operator op( \ + const scalar_operator &other) { \ if (this->constant_value.has_value() && \ other.constant_value.has_value()) { \ this->constant_value.value() op other.constant_value.value(); \ return *this; \ } \ auto newGenerator = \ - [other, *this]( \ - std::map> parameters) { \ + [other, \ + *this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ this->generator = newGenerator; \ @@ -184,7 +182,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ scalar_operator operator op(scalar_operator &&self, otherTy other) { \ - return std::move(self op##= other); \ + return std::move(self op## = other); \ } ARITHMETIC_OPERATIONS_RVALUE(*, double); @@ -204,10 +202,10 @@ ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); return scalar_operator(other op self.constant_value.value()); \ } \ auto newGenerator = \ - [other, generator = self.generator]( \ - std::map> parameters) { \ - return other op generator(parameters); \ - }; \ + [other, generator = self.generator]( \ + std::map> parameters) { \ + return other op generator(parameters); \ + }; \ return scalar_operator(newGenerator); \ } diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 2a999beb8c..ecf1b7241d 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -6,9 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -//#include -#include +// #include #include +#include namespace cudaq { @@ -16,113 +16,154 @@ class scalar_operator; class matrix_operator; -template +template class product_operator; -template +template class operator_sum; -template -product_operator operator*(double other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -operator_sum operator-(const HandlerTy &other, const product_operator &self); - -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self); +template +product_operator operator*(double other, + const product_operator &self); +template +operator_sum operator+(double other, + const product_operator &self); +template +operator_sum operator-(double other, + const product_operator &self); +template +product_operator operator*(std::complex other, + const product_operator &self); +template +operator_sum operator+(std::complex other, + const product_operator &self); +template +operator_sum operator-(std::complex other, + const product_operator &self); +template +product_operator operator*(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, + const product_operator &self); +template +product_operator operator*(const HandlerTy &other, + const product_operator &self); +template +operator_sum operator+(const HandlerTy &other, + const product_operator &self); +template +operator_sum operator-(const HandlerTy &other, + const product_operator &self); +template +operator_sum operator*(double other, + const operator_sum &self); +template +operator_sum operator+(double other, + const operator_sum &self); +template +operator_sum operator-(double other, + const operator_sum &self); +template +operator_sum operator*(std::complex other, + const operator_sum &self); +template +operator_sum operator+(std::complex other, + const operator_sum &self); +template +operator_sum operator-(std::complex other, + const operator_sum &self); +template +operator_sum operator*(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator*(const HandlerTy &other, + const operator_sum &self); +template +operator_sum operator+(const HandlerTy &other, + const operator_sum &self); +template +operator_sum operator-(const HandlerTy &other, + const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES -extern template -product_operator operator*(double other, const product_operator &self); -extern template -operator_sum operator+(double other, const product_operator &self); -extern template -operator_sum operator-(double other, const product_operator &self); -extern template -product_operator operator*(std::complex other, const product_operator &self); -extern template -operator_sum operator+(std::complex other, const product_operator &self); -extern template -operator_sum operator-(std::complex other, const product_operator &self); -extern template -product_operator operator*(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -extern template -product_operator operator*(const matrix_operator &other, const product_operator &self); -extern template -operator_sum operator+(const matrix_operator &other, const product_operator &self); -extern template -operator_sum operator-(const matrix_operator &other, const product_operator &self); +extern template product_operator +operator*(double other, const product_operator &self); +extern template operator_sum +operator+(double other, const product_operator &self); +extern template operator_sum +operator-(double other, const product_operator &self); +extern template product_operator +operator*(std::complex other, + const product_operator &self); +extern template operator_sum +operator+(std::complex other, + const product_operator &self); +extern template operator_sum +operator-(std::complex other, + const product_operator &self); +extern template product_operator +operator*(const scalar_operator &other, + const product_operator &self); +extern template operator_sum +operator+(const scalar_operator &other, + const product_operator &self); +extern template operator_sum +operator-(const scalar_operator &other, + const product_operator &self); +extern template product_operator +operator*(const matrix_operator &other, + const product_operator &self); +extern template operator_sum +operator+(const matrix_operator &other, + const product_operator &self); +extern template operator_sum +operator-(const matrix_operator &other, + const product_operator &self); -extern template -operator_sum operator*(double other, const operator_sum &self); -extern template -operator_sum operator+(double other, const operator_sum &self); -extern template -operator_sum operator-(double other, const operator_sum &self); -extern template -operator_sum operator*(std::complex other, const operator_sum &self); -extern template -operator_sum operator+(std::complex other, const operator_sum &self); -extern template -operator_sum operator-(std::complex other, const operator_sum &self); -extern template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator*(const matrix_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const matrix_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const matrix_operator &other, const operator_sum &self); +extern template operator_sum +operator*(double other, const operator_sum &self); +extern template operator_sum +operator+(double other, const operator_sum &self); +extern template operator_sum +operator-(double other, const operator_sum &self); +extern template operator_sum +operator*(std::complex other, + const operator_sum &self); +extern template operator_sum +operator+(std::complex other, + const operator_sum &self); +extern template operator_sum +operator-(std::complex other, + const operator_sum &self); +extern template operator_sum +operator*(const scalar_operator &other, + const operator_sum &self); +extern template operator_sum +operator+(const scalar_operator &other, + const operator_sum &self); +extern template operator_sum +operator-(const scalar_operator &other, + const operator_sum &self); +extern template operator_sum +operator*(const matrix_operator &other, + const operator_sum &self); +extern template operator_sum +operator+(const matrix_operator &other, + const operator_sum &self); +extern template operator_sum +operator-(const matrix_operator &other, + const operator_sum &self); #endif -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index b450f77e44..2a9281ace9 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,22 +8,21 @@ #pragma once -#include "dynamics/templates.h" #include "definition.h" +#include "dynamics/templates.h" #include "utils/tensor.h" +#include #include #include #include #include -#include #include namespace cudaq { class MatrixArithmetics; - class scalar_operator { private: @@ -37,7 +36,6 @@ class scalar_operator { ScalarCallbackFunction generator; public: - // constructors and destructors scalar_operator(double value); @@ -64,21 +62,22 @@ class scalar_operator { // assignments // assignment operator - scalar_operator& operator=(const scalar_operator &other); + scalar_operator &operator=(const scalar_operator &other); // move assignment operator - scalar_operator& operator=(scalar_operator &&other); + scalar_operator &operator=(scalar_operator &&other); // evaluations /// @brief Return the scalar operator as a concrete complex value. - std::complex - evaluate(const std::map> parameters = {}) const; + std::complex evaluate( + const std::map> parameters = {}) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions = {}, - const std::map> parameters = {}) const; + matrix_2 to_matrix( + const std::map dimensions = {}, + const std::map> parameters = {}) const; // comparisons @@ -95,36 +94,40 @@ class scalar_operator { scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; scalar_operator operator-(double other) const; - scalar_operator& operator*=(double other); - scalar_operator& operator/=(double other); - scalar_operator& operator+=(double other); - scalar_operator& operator-=(double other); + scalar_operator &operator*=(double other); + scalar_operator &operator/=(double other); + scalar_operator &operator+=(double other); + scalar_operator &operator-=(double other); scalar_operator operator*(std::complex other) const; scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - scalar_operator& operator*=(std::complex other); - scalar_operator& operator/=(std::complex other); - scalar_operator& operator+=(std::complex other); - scalar_operator& operator-=(std::complex other); + scalar_operator &operator*=(std::complex other); + scalar_operator &operator/=(std::complex other); + scalar_operator &operator+=(std::complex other); + scalar_operator &operator-=(std::complex other); scalar_operator operator*(const scalar_operator &other) const; scalar_operator operator/(const scalar_operator &other) const; scalar_operator operator+(const scalar_operator &other) const; scalar_operator operator-(const scalar_operator &other) const; - scalar_operator& operator*=(const scalar_operator &other); - scalar_operator& operator/=(const scalar_operator &other); - scalar_operator& operator+=(const scalar_operator &other); - scalar_operator& operator-=(const scalar_operator &other); + scalar_operator &operator*=(const scalar_operator &other); + scalar_operator &operator/=(const scalar_operator &other); + scalar_operator &operator+=(const scalar_operator &other); + scalar_operator &operator-=(const scalar_operator &other); /// TODO: implement and test pow friend scalar_operator operator*(scalar_operator &&self, double other); friend scalar_operator operator/(scalar_operator &&self, double other); friend scalar_operator operator+(scalar_operator &&self, double other); friend scalar_operator operator-(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, std::complex other); - friend scalar_operator operator/(scalar_operator &&self, std::complex other); - friend scalar_operator operator+(scalar_operator &&self, std::complex other); - friend scalar_operator operator-(scalar_operator &&self, std::complex other); + friend scalar_operator operator+(scalar_operator &&self, + std::complex other); + friend scalar_operator operator/(scalar_operator &&self, + std::complex other); + friend scalar_operator operator+(scalar_operator &&self, + std::complex other); + friend scalar_operator operator-(scalar_operator &&self, + std::complex other); // left-hand arithmetics @@ -132,13 +135,16 @@ class scalar_operator { friend scalar_operator operator/(double other, const scalar_operator &self); friend scalar_operator operator+(double other, const scalar_operator &self); friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, const scalar_operator &self); - friend scalar_operator operator/(std::complex other, const scalar_operator &self); - friend scalar_operator operator+(std::complex other, const scalar_operator &self); - friend scalar_operator operator-(std::complex other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, + const scalar_operator &self); + friend scalar_operator operator/(std::complex other, + const scalar_operator &self); + friend scalar_operator operator+(std::complex other, + const scalar_operator &self); + friend scalar_operator operator-(std::complex other, + const scalar_operator &self); }; - /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -147,28 +153,26 @@ template // handler needs to inherit from operation_handler class operator_sum { private: - std::tuple, std::vector> m_canonicalize_product(product_operator &prod) const; std::tuple, std::vector> m_canonical_terms() const; - matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms = true) const; void aggregate_terms(); - template - void aggregate_terms(const product_operator &head, Args&& ... args); + template + void aggregate_terms(const product_operator &head, Args &&...args); protected: - operator_sum() = default; std::vector> terms; std::vector coefficients; public: - // read-only properties /// @brief The degrees of freedom that the operator acts on in canonical @@ -182,8 +186,12 @@ class operator_sum { // constructors and destructors - template, Args>...>::value, void>> - operator_sum(const Args&... args); + template , Args>...>::value, + void>> + operator_sum(const Args &...args); operator_sum(const std::vector> &terms); @@ -200,10 +208,10 @@ class operator_sum { // assignments // assignment operator - operator_sum& operator=(const operator_sum &other); + operator_sum &operator=(const operator_sum &other); // move assignment operator - operator_sum& operator=(operator_sum &&other); + operator_sum &operator=(operator_sum &&other); // evaluations @@ -217,20 +225,21 @@ class operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(const std::map &dimensions = {}, - const std::map> ¶meters = {}) const; + matrix_2 to_matrix( + const std::map &dimensions = {}, + const std::map> ¶meters = {}) const; // comparisons - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. + /// @brief True, if the other value is an operator_sum with + /// equivalent terms, and False otherwise. The equality takes into account + /// that operator addition is commutative, as is the product of two operators + /// if they act on different degrees of freedom. The equality comparison does + /// *not* take commutation relations into account, and does not try to reorder + /// terms `blockwise`; it may hence evaluate to False, even if two operators + /// in reality are the same. If the equality evaluates to True, on the other + /// hand, the operators are guaranteed to represent the same transformation + /// for all arguments. bool operator==(const operator_sum &other) const; // unary operators @@ -252,88 +261,100 @@ class operator_sum { operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; + operator_sum + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const; + operator_sum + operator-(const product_operator &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; - operator_sum& operator*=(double other); - operator_sum& operator+=(double other); - operator_sum& operator-=(double other); - operator_sum& operator*=(std::complex other); - operator_sum& operator+=(std::complex other); - operator_sum& operator-=(std::complex other); - operator_sum& operator*=(const scalar_operator &other); - operator_sum& operator+=(const scalar_operator &other); - operator_sum& operator-=(const scalar_operator &other); - operator_sum& operator*=(const HandlerTy &other); - operator_sum& operator+=(const HandlerTy &other); - operator_sum& operator-=(const HandlerTy &other); - operator_sum& operator*=(const product_operator &other); - operator_sum& operator+=(const product_operator &other); - operator_sum& operator-=(const product_operator &other); - operator_sum& operator*=(const operator_sum &other); - operator_sum& operator+=(const operator_sum &other); - operator_sum& operator-=(const operator_sum &other); + operator_sum &operator*=(double other); + operator_sum &operator+=(double other); + operator_sum &operator-=(double other); + operator_sum &operator*=(std::complex other); + operator_sum &operator+=(std::complex other); + operator_sum &operator-=(std::complex other); + operator_sum &operator*=(const scalar_operator &other); + operator_sum &operator+=(const scalar_operator &other); + operator_sum &operator-=(const scalar_operator &other); + operator_sum &operator*=(const HandlerTy &other); + operator_sum &operator+=(const HandlerTy &other); + operator_sum &operator-=(const HandlerTy &other); + operator_sum &operator*=(const product_operator &other); + operator_sum &operator+=(const product_operator &other); + operator_sum &operator-=(const product_operator &other); + operator_sum &operator*=(const operator_sum &other); + operator_sum &operator+=(const operator_sum &other); + operator_sum &operator-=(const operator_sum &other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. - template + // Being a bit permissive here, since otherwise the explicit template + // instantiation is a nightmare. + template friend operator_sum operator*(double other, const operator_sum &self); - template + template friend operator_sum operator+(double other, const operator_sum &self); - template + template friend operator_sum operator-(double other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, const operator_sum &self); - template - friend operator_sum operator+(std::complex other, const operator_sum &self); - template - friend operator_sum operator-(std::complex other, const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - template + template + friend operator_sum operator*(std::complex other, + const operator_sum &self); + template + friend operator_sum operator+(std::complex other, + const operator_sum &self); + template + friend operator_sum operator-(std::complex other, + const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, + const operator_sum &self); + template friend operator_sum operator*(const T &other, const operator_sum &self); - template + template friend operator_sum operator+(const T &other, const operator_sum &self); - template - friend operator_sum operator-(const T &other, const operator_sum &self); - - template - friend operator_sum product_operator::operator*(const operator_sum &other) const; - template - friend operator_sum product_operator::operator+(const operator_sum &other) const; - template - friend operator_sum product_operator::operator-(const operator_sum &other) const; + template + friend operator_sum operator-(const T &other, const operator_sum &self); + + template + friend operator_sum + product_operator::operator*(const operator_sum &other) const; + template + friend operator_sum + product_operator::operator+(const operator_sum &other) const; + template + friend operator_sum + product_operator::operator-(const operator_sum &other) const; }; - /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types /// that can. template // handler needs to inherit from operation_handler class product_operator : public operator_sum { -friend class operator_sum; // FIXME: explicitly list members instead? + friend class operator_sum; // FIXME: explicitly list members + // instead? private: - void aggregate_terms(); - template - void aggregate_terms(const HandlerTy &head, Args&& ... args); + template + void aggregate_terms(const HandlerTy &head, Args &&...args); - matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms = true) const; public: - // read-only properties /// @brief The degrees of freedom that the operator acts on in canonical @@ -350,12 +371,17 @@ friend class operator_sum; // FIXME: explicitly list members instead? // constructors and destructors - template...>::value, void>> - product_operator(scalar_operator coefficient, const Args&... args); + template < + class... Args, + class = std::enable_if_t< + std::conjunction...>::value, void>> + product_operator(scalar_operator coefficient, const Args &...args); - product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + product_operator(scalar_operator coefficient, + const std::vector &atomic_operators); - product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + product_operator(scalar_operator coefficient, + std::vector &&atomic_operators); // copy constructor product_operator(const product_operator &other); @@ -368,10 +394,11 @@ friend class operator_sum; // FIXME: explicitly list members instead? // assignments // assignment operator - product_operator& operator=(const product_operator &other); + product_operator & + operator=(const product_operator &other); // move assignment operator - product_operator& operator=(product_operator &&other); + product_operator &operator=(product_operator &&other); // evaluations @@ -385,12 +412,14 @@ friend class operator_sum; // FIXME: explicitly list members instead? /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; + matrix_2 + to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; // comparisons - /// @brief True, if the other value is an operator_sum with equivalent terms, + /// @brief True, if the other value is an operator_sum with + /// equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. @@ -420,49 +449,65 @@ friend class operator_sum; // FIXME: explicitly list members instead? product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; + product_operator + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const; + operator_sum + operator-(const product_operator &other) const; operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - product_operator& operator*=(double other); - product_operator& operator*=(std::complex other); - product_operator& operator*=(const scalar_operator &other); - product_operator& operator*=(const HandlerTy &other); - product_operator& operator*=(const product_operator &other); + product_operator &operator*=(double other); + product_operator &operator*=(std::complex other); + product_operator &operator*=(const scalar_operator &other); + product_operator &operator*=(const HandlerTy &other); + product_operator & + operator*=(const product_operator &other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. - template - friend product_operator operator*(double other, const product_operator &self); - template - friend operator_sum operator+(double other, const product_operator &self); - template - friend operator_sum operator-(double other, const product_operator &self); - template - friend product_operator operator*(std::complex other, const product_operator &self); - template - friend operator_sum operator+(std::complex other, const product_operator &self); - template - friend operator_sum operator-(std::complex other, const product_operator &self); - template - friend product_operator operator*(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - template - friend product_operator operator*(const T &other, const product_operator &self); - template - friend operator_sum operator+(const T &other, const product_operator &self); - template - friend operator_sum operator-(const T &other, const product_operator &self); + // Being a bit permissive here, since otherwise the explicit template + // instantiation is a nightmare. + template + friend product_operator operator*(double other, + const product_operator &self); + template + friend operator_sum operator+(double other, + const product_operator &self); + template + friend operator_sum operator-(double other, + const product_operator &self); + template + friend product_operator operator*(std::complex other, + const product_operator &self); + template + friend operator_sum operator+(std::complex other, + const product_operator &self); + template + friend operator_sum operator-(std::complex other, + const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, + const product_operator &self); + template + friend product_operator operator*(const T &other, + const product_operator &self); + template + friend operator_sum operator+(const T &other, + const product_operator &self); + template + friend operator_sum operator-(const T &other, + const product_operator &self); }; - class matrix_operator { private: @@ -476,22 +521,22 @@ class matrix_operator { /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. matrix_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), degrees(degrees) {} + : id(operator_id), degrees(degrees) {} // constructor matrix_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), degrees(std::move(degrees)) {} + : id(operator_id), degrees(std::move(degrees)) {} // copy constructor matrix_operator(const matrix_operator &other) - : degrees(other.degrees), id(other.id) {} + : degrees(other.degrees), id(other.id) {} // move constructor - matrix_operator(matrix_operator &&other) - : degrees(std::move(other.degrees)), id(other.id) {} + matrix_operator(matrix_operator &&other) + : degrees(std::move(other.degrees)), id(other.id) {} // assignment operator - matrix_operator& operator=(const matrix_operator& other) { + matrix_operator &operator=(const matrix_operator &other) { if (this != &other) { degrees = other.degrees; id = other.id; @@ -500,9 +545,9 @@ class matrix_operator { } // move assignment operator - matrix_operator& operator=(matrix_operator &&other) { + matrix_operator &operator=(matrix_operator &&other) { degrees = std::move(other.degrees); - id = other.id; + id = other.id; return *this; } @@ -521,8 +566,9 @@ class matrix_operator { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; + matrix_2 + to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. @@ -569,9 +615,11 @@ class matrix_operator { /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - static void define(std::string operator_id, std::vector expected_dimensions, - CallbackFunction &&create) { - auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); + static void define(std::string operator_id, + std::vector expected_dimensions, + CallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, + std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { // todo: make a nice error message to say op already exists @@ -581,7 +629,8 @@ class matrix_operator { }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system -class rydberg_hamiltonian : public operator_sum { +template +class rydberg_hamiltonian : public operator_sum { public: using Coordinate = std::pair; @@ -633,7 +682,6 @@ extern template class product_operator; extern template class operator_sum; #endif - template class OperatorArithmetics { public: @@ -653,7 +701,7 @@ class OperatorArithmetics { }; class EvaluatedMatrix { -friend class MatrixArithmetics; + friend class MatrixArithmetics; private: std::vector m_degrees; @@ -685,7 +733,8 @@ class MatrixArithmetics : public OperatorArithmetics { public: std::map &m_dimensions; // fixme: make const - std::map> &m_parameters; // fixme: make const + std::map> + &m_parameters; // fixme: make const MatrixArithmetics(std::map dimensions, std::map> parameters) @@ -702,8 +751,9 @@ class MatrixArithmetics : public OperatorArithmetics { EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); // Computes the matrix of an ElementaryOperator or ScalarOperator using its // `to_matrix` method. - EvaluatedMatrix - evaluate(std::variant> op); + EvaluatedMatrix evaluate(std::variant> + op); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 18486385fa..5659b01d32 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -107,7 +107,7 @@ class matrix_2 { /// Return a square identity matrix for the given size. static matrix_2 identity(const std::size_t rows); - + /// Kronecker a list of matrices. The list can be any container that has /// iterators defined. template diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 5acde43b42..d7710419fb 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -103,11 +103,13 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } -void assert_product_equal(const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { +void assert_product_equal( + const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + auto sumterms_prod = + ((const cudaq::operator_sum &)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); @@ -120,9 +122,7 @@ void assert_product_equal(const cudaq::product_operator /// the return of this function, since the `-1.0` value /// is going out of scope somewhere down the line in its /// conversion behind the scenes to a scalar operator. -cudaq::scalar_operator negate(cudaq::scalar_operator op) { - return -1.0 * op; -} +cudaq::scalar_operator negate(cudaq::scalar_operator op) { return -1.0 * op; } TEST(OperatorExpressions, checkElementaryAgainstDouble) { std::complex value = 0.125 + 0.125j; @@ -135,7 +135,7 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { auto sum = value + elementary; auto reverse = elementary + value; - auto got_matrix = sum.to_matrix({{0,3}}); + auto got_matrix = sum.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils_0::id_matrix(3); @@ -146,14 +146,15 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `matrix_operator` - `complex` and `complex` - `matrix_operator` + // `matrix_operator` - `complex` and `complex` - + // `matrix_operator` { auto elementary = cudaq::matrix_operator::position(0); auto difference = value - elementary; auto reverse = elementary - value; - auto got_matrix = difference.to_matrix({{0,3}}); + auto got_matrix = difference.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils_0::id_matrix(3); @@ -172,7 +173,7 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { auto product = value * elementary; auto reverse = elementary * value; - auto got_matrix = product.to_matrix({{0,3}}); + auto got_matrix = product.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils_0::id_matrix(3); @@ -229,8 +230,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::parity_matrix(level_count); @@ -253,7 +256,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto got_matrix = sum.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils_0::number_matrix(level_count); + auto want_reverse_matrix = + scaled_identity - utils_0::number_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -270,10 +274,13 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::position_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils_0::position_matrix(level_count); + auto want_reverse_matrix = + scaled_identity - utils_0::position_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -286,8 +293,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + utils_0::assert_product_equal(product, const_scale_factor, + {cudaq::matrix_operator("momentum", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, + {cudaq::matrix_operator("momentum", {0})}); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -311,16 +320,20 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate(), {cudaq::matrix_operator("create", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), + {cudaq::matrix_operator("create", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), + {cudaq::matrix_operator("create", {0})}); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = scaled_identity * utils_0::create_matrix(level_count); @@ -452,8 +465,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -469,7 +482,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; utils_0::checkEqual(want_matrix, got_matrix); @@ -480,8 +494,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -496,8 +510,9 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; utils_0::checkEqual(want_matrix, got_matrix); @@ -530,8 +545,10 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + {{"squeezing", value}}); + auto got_reverse_matrix = reverse.to_matrix( + {{0, level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; utils_0::checkEqual(want_matrix, got_matrix); @@ -540,8 +557,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// `operator_sum += matrix_operator` { - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum += cudaq::matrix_operator::displace(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -554,15 +571,16 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}, {{"displacement", value}}); + auto got_matrix = operator_sum.to_matrix( + {{0, level_count}, {1, level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; utils_0::checkEqual(want_matrix, got_matrix); } /// `operator_sum -= matrix_operator` { - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum -= cudaq::matrix_operator::annihilate(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -574,7 +592,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = + operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = term_0_full + term_1_full - self_full; utils_0::checkEqual(want_matrix, got_matrix); } @@ -582,8 +601,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// `operator_sum *= matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum *= self; @@ -599,7 +618,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = + operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = sum_full * self_full; utils_0::checkEqual(want_matrix, got_matrix); } diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp index 117fe51f48..9d7548b720 100644 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -216,10 +216,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { } } -//TEST(OperatorExpressions, checkCustomElementaryOps) { - // pass +// TEST(OperatorExpressions, checkCustomElementaryOps) { +// pass - // ex: - // operator acts upon {0,2} - // user gives us dimensions for {0,1,2} +// ex: +// operator acts upon {0,2} +// user gives us dimensions for {0,1,2} //} diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index abbe10a52f..c959e1f7c7 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -238,8 +238,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -257,8 +257,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -277,8 +279,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` { level_count = 2; - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -286,9 +288,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2,level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -306,8 +308,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -315,8 +317,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -334,8 +338,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum *= scalar_operator` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::momentum(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::momentum(2); sum *= cudaq::scalar_operator(value); @@ -345,7 +349,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils_2::id_matrix(level_count + 1), @@ -355,7 +360,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils_2::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils_2::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils_2::checkEqual(want_matrix, got_matrix); @@ -363,14 +369,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum += scalar_operator` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::position(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::position(2); sum += cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils_2::id_matrix(level_count + 1), @@ -380,7 +387,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils_2::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils_2::id_matrix((level_count + 1) * level_count); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils_2::checkEqual(want_matrix, got_matrix); @@ -395,7 +403,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils_2::id_matrix(level_count + 1), @@ -405,7 +414,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils_2::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils_2::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) - scaled_identity; utils_2::checkEqual(want_matrix, got_matrix); @@ -419,8 +429,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` and `double * operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -430,16 +440,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } - auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -466,8 +480,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -485,8 +500,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - double` and `double - operator_sum` { - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::number(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::number(2); auto difference = original - double_value; auto reverse = double_value - original; @@ -494,8 +509,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::parity_matrix(level_count)); @@ -513,18 +530,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= double` { - auto sum = cudaq::matrix_operator::squeeze(1) + - cudaq::matrix_operator::squeeze(2); + auto sum = + cudaq::matrix_operator::squeeze(1) + cudaq::matrix_operator::squeeze(2); sum *= double_value; ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -542,14 +561,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum += double_value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -566,14 +585,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum -= double_value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -591,8 +610,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` and `std::complex * // operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * value; auto reverse = value * sum; @@ -610,8 +629,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = product.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -630,8 +651,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` and `std::complex + // operator_sum` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = original + value; auto reverse = value + original; @@ -639,8 +660,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -659,8 +681,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` and `std::complex - // operator_sum` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto difference = original - value; auto reverse = value - original; @@ -668,8 +690,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -687,8 +711,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= std::complex` { - auto sum = cudaq::matrix_operator::displace(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::displace(1) + cudaq::matrix_operator::parity(2); sum *= value; @@ -698,7 +722,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"displacement", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"displacement", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -722,7 +747,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -739,14 +765,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= std::complex` { - auto sum = cudaq::matrix_operator::position(1) + - cudaq::matrix_operator::number(2); + auto sum = + cudaq::matrix_operator::position(1) + cudaq::matrix_operator::number(2); sum -= value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::position_matrix(level_count)); @@ -766,8 +792,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { - auto sum_0 = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum_0 = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::create(3); @@ -776,7 +802,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 5); - auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = sum.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -819,8 +848,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum - operator_sum` { - auto sum_0 = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::position(2); + auto sum_0 = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::position(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::momentum(3); @@ -829,7 +858,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 5); - auto got_matrix = difference.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = difference.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -872,8 +904,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum * operator_sum` { - auto sum_0 = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum_0 = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::create(3); @@ -888,8 +920,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum_product_reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); - auto got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = sum_product.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); + auto got_matrix_reverse = + sum_product_reverse.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -934,8 +973,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum *= operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::create(3); @@ -946,7 +985,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = sum.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -998,14 +1040,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum += product; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1021,8 +1064,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1039,14 +1081,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum -= product; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1062,8 +1105,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1080,8 +1122,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum *= product; @@ -1090,7 +1132,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(term.n_terms() == 3); } - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1106,8 +1149,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index cb4838fd6e..4155178376 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -105,11 +105,13 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } -void assert_product_equal(const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { +void assert_product_equal( + const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + auto sumterms_prod = + ((const cudaq::operator_sum &)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); @@ -118,7 +120,7 @@ void assert_product_equal(const cudaq::product_operator void print(cudaq::matrix_2 matrix) { for (std::size_t i = 0; i < matrix.get_rows(); i++) { for (std::size_t j = 0; j < matrix.get_columns(); j++) { - std::cout << matrix[{i,j}] << " "; + std::cout << matrix[{i, j}] << " "; } std::cout << std::endl; } @@ -137,7 +139,10 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; - utils_1::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {0}), cudaq::matrix_operator("create", {0})}); + utils_1::assert_product_equal( + got, 1., + {cudaq::matrix_operator("annihilate", {0}), + cudaq::matrix_operator("create", {0})}); auto got_matrix = got.to_matrix({{0, level_count}}); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -164,7 +169,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got_reverse.degrees() == want_degrees); auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {1, level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -194,8 +200,9 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {2, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {2, level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -225,8 +232,10 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -314,15 +323,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), utils_1::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = value_0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; @@ -346,8 +357,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -379,8 +391,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -412,8 +425,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -445,8 +460,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -478,8 +495,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::momentum_matrix(level_count)); @@ -498,10 +517,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * complex` { - auto product_op = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::number(1); + auto product_op = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == + std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; @@ -515,8 +535,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -535,25 +556,29 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * double` { - auto product_op = cudaq::matrix_operator::parity(0) * - cudaq::matrix_operator::parity(1); + auto product_op = + cudaq::matrix_operator::parity(0) * cudaq::matrix_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == + std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); - ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::parity_matrix(level_count)); @@ -587,8 +612,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::position_matrix(level_count)); @@ -607,8 +633,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= complex` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::momentum(1); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.n_terms() == 2); @@ -617,7 +643,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -639,12 +665,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= 2.0; ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -660,8 +687,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= scalar_operator` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::momentum(1); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::momentum(1); auto scalar_op = cudaq::scalar_operator(value_0); product *= scalar_op; @@ -673,7 +700,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -691,7 +718,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; - std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::map dimensions = { + {0, level_count}, {1, level_count}, {2, level_count + 1}}; // `product_operator + product_operator` { @@ -742,8 +770,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::number(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::momentum(2); + auto term_1 = + cudaq::matrix_operator::create(1) * cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -784,8 +812,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::annihilate(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::parity(2); + auto term_1 = + cudaq::matrix_operator::create(1) * cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -881,8 +909,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -911,8 +940,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -941,8 +972,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -969,7 +1001,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -988,14 +1020,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { int level_count = 3; - std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::map dimensions = { + {0, level_count}, {1, level_count}, {2, level_count + 1}}; // `product_operator + operator_sum` { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto original_sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original_sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = product + original_sum; auto reverse = original_sum + product; @@ -1038,8 +1071,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto original_difference = cudaq::matrix_operator::create(1) - - cudaq::matrix_operator::create(2); + auto original_difference = + cudaq::matrix_operator::create(1) - cudaq::matrix_operator::create(2); auto difference = product - original_difference; auto reverse = original_difference - product; @@ -1047,7 +1080,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix(dimensions); + auto got_matrix = difference.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. @@ -1082,8 +1115,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { { auto original_product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = original_product * sum; auto reverse = sum * original_product; @@ -1091,7 +1124,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = product.to_matrix(dimensions); + auto got_matrix = product.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. From c0ffcc7f6f47f81feac9454ee48bacd4895d8f41 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 09:28:42 -0800 Subject: [PATCH 082/311] Fixing Rydberg with template HandlerTy Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/rydberg_hamiltonian.cpp | 24 ++++++++++---- unittests/dynamics/rydberg_hamiltonian.cpp | 33 ++++++++++--------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp index 3d8b125ad3..668c9a23db 100644 --- a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp +++ b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp @@ -11,7 +11,8 @@ #include namespace cudaq { -rydberg_hamiltonian::rydberg_hamiltonian( +template +rydberg_hamiltonian::rydberg_hamiltonian( const std::vector &atom_sites, const scalar_operator &litude, const scalar_operator &phase, const scalar_operator &delta_global, const std::vector &atom_filling, @@ -34,22 +35,31 @@ rydberg_hamiltonian::rydberg_hamiltonian( } } -const std::vector & -rydberg_hamiltonian::get_atom_sites() const { +template +const std::vector::Coordinate> & +rydberg_hamiltonian::get_atom_sites() const { return atom_sites; } -const std::vector &rydberg_hamiltonian::get_atom_filling() const { +template +const std::vector & +rydberg_hamiltonian::get_atom_filling() const { return atom_filling; } -const scalar_operator &rydberg_hamiltonian::get_amplitude() const { +template +const scalar_operator &rydberg_hamiltonian::get_amplitude() const { return amplitude; } -const scalar_operator &rydberg_hamiltonian::get_phase() const { return phase; } +template +const scalar_operator &rydberg_hamiltonian::get_phase() const { + return phase; +} -const scalar_operator &rydberg_hamiltonian::get_delta_global() const { +template +const scalar_operator & +rydberg_hamiltonian::get_delta_global() const { return delta_global; } } // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp index 2e64cb0666..ef2856c1a6 100644 --- a/unittests/dynamics/rydberg_hamiltonian.cpp +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -13,7 +13,7 @@ using namespace cudaq; TEST(RydbergHamiltonianTest, ConstructorValidInputs) { // Valid atom sites - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators @@ -22,7 +22,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { scalar_operator delta_global(-0.5); // Valid atom filling - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); @@ -35,7 +36,7 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { } TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -46,15 +47,15 @@ TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { // Valid atom filling std::vector atom_filling = {1, 0, 1}; - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global, - atom_filling); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global, atom_filling); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); } TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -65,13 +66,13 @@ TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { // Invalid atom filling size std::vector atom_filling = {1, 0}; - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, - atom_filling), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, + delta_global, atom_filling), std::invalid_argument); } TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -83,13 +84,13 @@ TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { auto delta_local = std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, - {}, delta_local), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, + delta_global, {}, delta_local), std::runtime_error); } TEST(RydbergHamiltonianTest, Accessors) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -97,7 +98,8 @@ TEST(RydbergHamiltonianTest, Accessors) { scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global); EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), @@ -109,7 +111,7 @@ TEST(RydbergHamiltonianTest, Accessors) { } TEST(RydbergHamiltonianTest, DefaultAtomFilling) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators @@ -117,7 +119,8 @@ TEST(RydbergHamiltonianTest, DefaultAtomFilling) { scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global); std::vector expected_filling(atom_sites.size(), 1); EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); From 8d67f303f7a6c526ad81f913e56926deb0bf7b4f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 09:42:06 -0800 Subject: [PATCH 083/311] Fixing with template typename Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 8 +++++--- unittests/dynamics/test_cudm_helpers.cpp | 23 ++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index a04869d28c..50e83c5fdf 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -173,10 +173,12 @@ convert_dimensions(const std::vector &mode_extents) { return dimensions; } +template cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, - const operator_sum &op, const std::vector &mode_extents) { + const operator_sum &op, + const std::vector &mode_extents) { if (op.get_terms().empty()) { throw std::invalid_argument("Operator sum cannot be empty."); } @@ -197,8 +199,8 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( mode_extents.data(), &term)); for (const auto &component : product_op.get_terms()) { - if (std::holds_alternative(component)) { - const auto &elem_op = std::get(component); + if (std::holds_alternative(component)) { + const auto &elem_op = std::get(component); auto subspace_extents = get_subspace_extents(mode_extents, elem_op.degrees); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index e747cc97db..dd322613bb 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,13 +12,14 @@ #include // Initialize operator_sum -cudaq::operator_sum initialize_operator_sum() { +template +cudaq::operator_sum initialize_operator_sum() { std::vector degrees = {0, 1}; // Elementary operators - cudaq::elementary_operator pauli_x("pauli_x", {0}); - cudaq::elementary_operator pauli_z("pauli_z", {1}); - cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); + cudaq::matrix_operator pauli_x("pauli_x", {0}); + cudaq::matrix_operator pauli_z("pauli_z", {1}); + cudaq::matrix_operator identity = cudaq::matrix_operator::identity(0); auto prod_op_1 = cudaq::scalar_operator(std::complex(1.0, 0.0)) * pauli_x * pauli_z; @@ -26,7 +27,7 @@ cudaq::operator_sum initialize_operator_sum() { auto prod_op_2 = cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; - cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); return op_sum; } @@ -88,11 +89,11 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, - mode_extents); + auto result = cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -103,9 +104,9 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, - op_sum, mode_extents), + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + invalid_handle, {}, op_sum, mode_extents), std::runtime_error); } From 83f71372bd44c0f676c27ef72c94499e3bb9393c Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 10:37:57 -0800 Subject: [PATCH 084/311] Fixing HandlerTy tenplate by setting it to a value Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 2 +- .../cudaq/dynamics/runge_kutta_integrator.cpp | 53 +++++++++++-------- runtime/cudaq/runge_kutta_integrator.h | 8 +-- unittests/dynamics/rydberg_hamiltonian.cpp | 48 ++++++++--------- unittests/dynamics/test_cudm_helpers.cpp | 2 +- .../dynamics/test_runge_kutta_integrator.cpp | 19 +++---- 6 files changed, 71 insertions(+), 61 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 1abe004c0d..bbbbeeb001 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,7 +16,7 @@ #include namespace cudaq { -template +template > class BaseIntegrator { protected: std::map integrator_options; diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index e966ef6ec8..d562f0ca26 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -12,50 +12,57 @@ using namespace cudaq; namespace cudaq { -void runge_kutta_integrator::integrate(double target_time) { - if (!stepper) { +template +void runge_kutta_integrator::integrate(double target_time) { + if (!this->stepper) { throw std::runtime_error("Time stepper is not initialized."); } - if (integrator_options.find("dt") == integrator_options.end()) { + if (this->integrator_options.find("dt") == this->integrator_options.end()) { throw std::invalid_argument( "Time step size (dt) is missing from integrator options."); } - double dt = integrator_options["dt"]; + double dt = this->integrator_options["dt"]; if (dt <= 0) { throw std::invalid_argument("Invalid time step size for integration."); } - while (t < target_time) { - double step_size = std::min(dt, target_time - t); + while (this->t < target_time) { + double step_size = std::min(dt, target_time - this->t); - std::cout << "Runge-Kutta step at time " << t + std::cout << "Runge-Kutta step at time " << this->t << " with step size: " << step_size << std::endl; - if (substeps_ == 1) { + if (this->substeps_ == 1) { // Euler method (1st order) - cudm_state k1 = stepper->compute(state, t, step_size); - state += k1; - } else if (substeps_ == 2) { + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + this->state += k1; + } else if (this->substeps_ == 2) { // Midpoint method (2nd order) - cudm_state k1 = stepper->compute(state, t, step_size / 2.0); - cudm_state k2 = stepper->compute(k1, t + step_size / 2.0, step_size); - state += (k1 + k2) * 0.5; - } else if (substeps_ == 4) { - // Runge-Kutta method (4th order) - cudm_state k1 = stepper->compute(state, t, step_size / 2.0); + cudm_state k1 = + this->stepper->compute(this->state, this->t, step_size / 2.0); cudm_state k2 = - stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); - cudm_state k3 = stepper->compute(k2, t + step_size / 2.0, step_size); - cudm_state k4 = stepper->compute(k3, t + step_size, step_size); - state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); + this->stepper->compute(k1, this->t + step_size / 2.0, step_size); + this->state += (k1 + k2) * 0.5; + } else if (this->substeps_ == 4) { + // Runge-Kutta method (4th order) + cudm_state k1 = + this->stepper->compute(this->state, this->t, step_size / 2.0); + cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, + step_size / 2.0); + cudm_state k3 = + this->stepper->compute(k2, this->t + step_size / 2.0, step_size); + cudm_state k4 = + this->stepper->compute(k3, this->t + step_size, step_size); + this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } // Update time - t += step_size; + this->t += step_size; } - std::cout << "Integration complete. Final time: " << t << std::endl; + std::cout << "Integration complete. Final time: " << this->t << std::endl; } + } // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index fa9585164f..aa45dc42ad 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -15,7 +15,8 @@ #include namespace cudaq { -class runge_kutta_integrator : public BaseIntegrator { +template > +class runge_kutta_integrator : public BaseIntegrator { public: /// @brief Constructor to initialize the Runge-Kutta integrator /// @param initial_state Initial quantum state. @@ -25,7 +26,8 @@ class runge_kutta_integrator : public BaseIntegrator { runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4) - : BaseIntegrator(std::move(initial_state), t0, stepper), + : BaseIntegrator(std::move(initial_state), t0, + stepper), substeps_(substeps) { if (!stepper) { throw std::invalid_argument("Time stepper must be initialized."); @@ -34,7 +36,7 @@ class runge_kutta_integrator : public BaseIntegrator { if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); } - post_init(); + this->post_init(); } /// @brief Perform Runge-Kutta integration until the target time. diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp index ef2856c1a6..33e7fdff71 100644 --- a/unittests/dynamics/rydberg_hamiltonian.cpp +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -13,8 +13,8 @@ using namespace cudaq; TEST(RydbergHamiltonianTest, ConstructorValidInputs) { // Valid atom sites - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -22,8 +22,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { scalar_operator delta_global(-0.5); // Valid atom filling - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global); + rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, + phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); @@ -36,8 +36,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { } TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -47,16 +47,16 @@ TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { // Valid atom filling std::vector atom_filling = {1, 0, 1}; - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global, atom_filling); + rydberg_hamiltonian> hamiltonian( + atom_sites, amplitude, phase, delta_global, atom_filling); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); } TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -66,14 +66,14 @@ TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { // Invalid atom filling size std::vector atom_filling = {1, 0}; - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, - delta_global, atom_filling), + EXPECT_THROW(rydberg_hamiltonian>( + atom_sites, amplitude, phase, delta_global, atom_filling), std::invalid_argument); } TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -84,22 +84,22 @@ TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { auto delta_local = std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, - delta_global, {}, delta_local), + EXPECT_THROW(rydberg_hamiltonian>( + atom_sites, amplitude, phase, delta_global, {}, delta_local), std::runtime_error); } TEST(RydbergHamiltonianTest, Accessors) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global); + rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, + phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), @@ -111,16 +111,16 @@ TEST(RydbergHamiltonianTest, Accessors) { } TEST(RydbergHamiltonianTest, DefaultAtomFilling) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global); + rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, + phase, delta_global); std::vector expected_filling(atom_sites.size(), 1); EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index dd322613bb..8c365bfb7c 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -16,7 +16,7 @@ template cudaq::operator_sum initialize_operator_sum() { std::vector degrees = {0, 1}; - // Elementary operators + // Matrix operators cudaq::matrix_operator pauli_x("pauli_x", {0}); cudaq::matrix_operator pauli_z("pauli_z", {1}); cudaq::matrix_operator identity = cudaq::matrix_operator::identity(0); diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 0200d01d8e..6de0c71db6 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -19,7 +19,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::shared_ptr time_stepper_; - std::unique_ptr integrator_; + std::unique_ptr> integrator_; std::unique_ptr state_; void SetUp() override { @@ -41,8 +41,9 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW(integrator_ = std::make_unique( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW(integrator_ = + std::make_unique>( + std::move(*state_), t0, time_stepper_, 4)); ASSERT_NE(integrator_, nullptr); } @@ -60,7 +61,7 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = std::make_unique( + auto eulerIntegrator = std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 1); eulerIntegrator->set_option("dt", 0.1); @@ -69,11 +70,11 @@ TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = std::make_unique( + auto midpointIntegrator = std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 2); - integrator_->set_option("dt", 0.1); - EXPECT_NO_THROW(integrator_->integrate(1.0)); + midpointIntegrator->set_option("dt", 0.1); + EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } // Integration with Runge-Kutta 4 (substeps = 4, which is the default value) @@ -109,7 +110,7 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = std::make_unique( + auto integrator_missing_dt = std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 2); @@ -141,7 +142,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique( + EXPECT_THROW(std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 3), From 193d636c5a52f0a23de5990672600374ec43cb81 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 13:49:48 -0800 Subject: [PATCH 085/311] Fixing unittests Signed-off-by: Sachin Pisal --- unittests/dynamics/test_cudm_helpers.cpp | 34 ++++++++++++------ .../dynamics/test_runge_kutta_integrator.cpp | 35 +++++++++++-------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 8c365bfb7c..ebfcc2d1af 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,22 +12,36 @@ #include // Initialize operator_sum -template +template > cudaq::operator_sum initialize_operator_sum() { std::vector degrees = {0, 1}; // Matrix operators cudaq::matrix_operator pauli_x("pauli_x", {0}); cudaq::matrix_operator pauli_z("pauli_z", {1}); - cudaq::matrix_operator identity = cudaq::matrix_operator::identity(0); + cudaq::product_operator identity = + cudaq::matrix_operator::identity(0); - auto prod_op_1 = cudaq::scalar_operator(std::complex(1.0, 0.0)) * - pauli_x * pauli_z; + std::vector identity_vec = identity.get_terms(); + auto identity_op = cudaq::product_operator( + cudaq::scalar_operator(1.0), identity_vec); + + std::vector pauli_x_vec = {pauli_x}; + auto prod_op_1 = cudaq::product_operator( + cudaq::scalar_operator(1.0), pauli_x_vec); + + std::vector pauli_z_vec = {pauli_z}; + prod_op_1 = prod_op_1 * cudaq::product_operator( + cudaq::scalar_operator(1.0), pauli_z_vec); auto prod_op_2 = - cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; + cudaq::product_operator( + cudaq::scalar_operator(std::complex(0.5, -0.5))) * + identity_op; - cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + std::vector> terms = { + prod_op_1, prod_op_2}; + cudaq::operator_sum op_sum(terms); return op_sum; } @@ -89,10 +103,10 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum>(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator( + auto result = cudaq::convert_to_cudensitymat_operator>( handle, {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); @@ -104,9 +118,9 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum>(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator>( invalid_handle, {}, op_sum, mode_extents), std::runtime_error); } diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 6de0c71db6..3180bd3c2b 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -19,7 +19,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::shared_ptr time_stepper_; - std::unique_ptr> integrator_; + std::unique_ptr>> integrator_; std::unique_ptr state_; void SetUp() override { @@ -41,9 +41,10 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW(integrator_ = - std::make_unique>( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW( + integrator_ = + std::make_unique>>( + std::move(*state_), t0, time_stepper_, 4)); ASSERT_NE(integrator_, nullptr); } @@ -61,18 +62,22 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = std::make_unique>( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 1); + auto eulerIntegrator = + std::make_unique>>( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 1); eulerIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); } // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = std::make_unique>( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto midpointIntegrator = + std::make_unique>>( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); midpointIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } @@ -110,9 +115,11 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = std::make_unique>( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto integrator_missing_dt = + std::make_unique>>( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); } @@ -142,7 +149,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique>( + EXPECT_THROW(std::make_unique>>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 3), From e4e46998c887dce1d3062723ef66f9e9f1abaf29 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 15:54:01 -0800 Subject: [PATCH 086/311] Adding missing functions in helpers and updating initialize_operator_sum in test_cudm_helpers Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 204 +++++++++++------------ runtime/cudaq/dynamics/helpers.h | 14 ++ unittests/dynamics/test_cudm_helpers.cpp | 33 +--- 3 files changed, 115 insertions(+), 136 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 5a3d2f8390..b2cff5eab2 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -17,135 +17,129 @@ namespace cudaq { namespace detail { -class _OperatorHelpers { -public: - _OperatorHelpers() = default; - - // Aggregate parameters from multiple mappings. - std::map - aggregate_parameters(const std::vector> - ¶meter_mappings) { - std::map parameter_descriptions; - - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; - } +// Aggregate parameters from multiple mappings. +std::map aggregate_parameters( + const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; } } - - return parameter_descriptions; } - // Extract documentation for a specific parameter from docstring. - std::string parameter_docs(const std::string ¶m_name, - const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; - } - - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); + return parameter_descriptions; +} - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; +// Extract documentation for a specific parameter from docstring. +std::string parameter_docs(const std::string ¶m_name, + const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if (std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); - } + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", + std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if (std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); } - } catch (...) { - return ""; } - + } catch (...) { return ""; } - // Extract positional arguments and keyword-only arguments. - std::pair, std::map> - args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); - } + return ""; +} + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> +args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); } + } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); - } + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); } - - return {extracted_args, kwonly_dict}; } - /// Generates all possible states for the given dimensions ordered according - /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector generate_all_states(std::vector degrees, - std::map dimensions) { - if (degrees.size() == 0) - return {}; - - std::vector states; - int range = dimensions[degrees[0]]; - for (auto state = 0; state < range; state++) { - states.push_back(std::to_string(state)); - } + return {extracted_args, kwonly_dict}; +} - for (auto idx = 1; idx < degrees.size(); ++idx) { - std::vector result; - for (auto current : states) { - for (auto state = 0; state < dimensions[degrees[idx]]; state++) { - result.push_back(current + std::to_string(state)); - } - } - states = result; - } +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector generate_all_states(std::vector degrees, + std::map dimensions) { + if (degrees.size() == 0) + return {}; - return states; + std::vector states; + int range = dimensions[degrees[0]]; + for (auto state = 0; state < range; state++) { + states.push_back(std::to_string(state)); } - cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation) { - auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); - std::vector> sorted_values; - for (std::size_t permuted : permutation) { - for (std::size_t permuted_again : permutation) { - sorted_values.push_back(matrix[{permuted, permuted_again}]); - } - } - int idx = 0; - for (std::size_t row = 0; row < result.get_rows(); row++) { - for (std::size_t col = 0; col < result.get_columns(); col++) { - result[{row, col}] = sorted_values[idx]; - idx++; + for (auto idx = 1; idx < degrees.size(); ++idx) { + std::vector result; + for (auto current : states) { + for (auto state = 0; state < dimensions[degrees[idx]]; state++) { + result.push_back(current + std::to_string(state)); } } - return result; + states = result; } - std::vector canonicalize_degrees(std::vector degrees) { - std::sort(degrees.begin(), degrees.end(), std::greater()); - return degrees; + return states; +} + +cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation) { + auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); + std::vector> sorted_values; + for (std::size_t permuted : permutation) { + for (std::size_t permuted_again : permutation) { + sorted_values.push_back(matrix[{permuted, permuted_again}]); + } } -}; + int idx = 0; + for (std::size_t row = 0; row < result.get_rows(); row++) { + for (std::size_t col = 0; col < result.get_columns(); col++) { + result[{row, col}] = sorted_values[idx]; + idx++; + } + } + return result; +} + +std::vector canonicalize_degrees(std::vector degrees) { + std::sort(degrees.begin(), degrees.end(), std::greater()); + return degrees; +} } // namespace detail } // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h index ec5446143e..51c8e4bff4 100644 --- a/runtime/cudaq/dynamics/helpers.h +++ b/runtime/cudaq/dynamics/helpers.h @@ -13,6 +13,20 @@ namespace cudaq { namespace detail { +// Aggregate parameters from multiple mappings. +std::map aggregate_parameters( + const std::vector> ¶meter_mappings); + +// Extract documentation for a specific parameter from docstring. +std::string parameter_docs(const std::string ¶m_name, + const std::string &docs); + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> +args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args); + /// Generates all possible states for the given dimensions ordered according /// to the sequence of degrees (ordering is relevant if dimensions differ). std::vector generate_all_states(std::vector degrees, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index ebfcc2d1af..482108e128 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,38 +12,9 @@ #include // Initialize operator_sum -template > +template cudaq::operator_sum initialize_operator_sum() { - std::vector degrees = {0, 1}; - - // Matrix operators - cudaq::matrix_operator pauli_x("pauli_x", {0}); - cudaq::matrix_operator pauli_z("pauli_z", {1}); - cudaq::product_operator identity = - cudaq::matrix_operator::identity(0); - - std::vector identity_vec = identity.get_terms(); - auto identity_op = cudaq::product_operator( - cudaq::scalar_operator(1.0), identity_vec); - - std::vector pauli_x_vec = {pauli_x}; - auto prod_op_1 = cudaq::product_operator( - cudaq::scalar_operator(1.0), pauli_x_vec); - - std::vector pauli_z_vec = {pauli_z}; - prod_op_1 = prod_op_1 * cudaq::product_operator( - cudaq::scalar_operator(1.0), pauli_z_vec); - - auto prod_op_2 = - cudaq::product_operator( - cudaq::scalar_operator(std::complex(0.5, -0.5))) * - identity_op; - - std::vector> terms = { - prod_op_1, prod_op_2}; - cudaq::operator_sum op_sum(terms); - - return op_sum; + return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); } class CuDensityMatTestFixture : public ::testing::Test { From cc62bac57a2f1550e937b6dc4b8220bafb731f83 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 08:24:56 -0800 Subject: [PATCH 087/311] fixing helpers.h path Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.h | 46 -------------------- runtime/cudaq/helpers.h | 73 +++++++++++++++----------------- 2 files changed, 34 insertions(+), 85 deletions(-) delete mode 100644 runtime/cudaq/dynamics/helpers.h diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h deleted file mode 100644 index 51c8e4bff4..0000000000 --- a/runtime/cudaq/dynamics/helpers.h +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/utils/tensor.h" -#include -#include - -namespace cudaq { -namespace detail { - -// Aggregate parameters from multiple mappings. -std::map aggregate_parameters( - const std::vector> ¶meter_mappings); - -// Extract documentation for a specific parameter from docstring. -std::string parameter_docs(const std::string ¶m_name, - const std::string &docs); - -// Extract positional arguments and keyword-only arguments. -std::pair, std::map> -args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args); - -/// Generates all possible states for the given dimensions ordered according -/// to the sequence of degrees (ordering is relevant if dimensions differ). -std::vector generate_all_states(std::vector degrees, - std::map dimensions); - -// Permutes the given matrix according to the given permutation. -// If states is the current order of vector entries on which the given matrix -// acts, and permuted_states is the desired order of an array on which the -// permuted matrix should act, then the permutation is defined such that -// [states[i] for i in permutation] produces permuted_states. -cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation); - -// Returns the degrees sorted in canonical order. -std::vector canonicalize_degrees(std::vector degrees); -} // namespace detail -} // namespace cudaq diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 48e7454adf..51c8e4bff4 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -1,4 +1,4 @@ -/****************************************************************-*- C++ -*-**** +/******************************************************************************* * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * @@ -6,46 +6,41 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#pragma once - -#include -#include -#include +#include "cudaq/utils/tensor.h" #include -#include -#include -#include -#include #include namespace cudaq { -class OperatorHelpers { -public: - // Aggregate parameters from multiple mappings. - static std::map - aggregate_parameters(const std::vector> - ¶meter_mappings); - - // Extract documentation for a specific parameter from docstring. - static std::string parameter_docs(const std::string ¶m_name, - const std::string &docs); - - // Extract positional arguments and keyword-only arguments. - static std::pair, std::map> - args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args); - - // Generate all possible quantum states for given degrees and dimensions. - static std::vector - generate_all_states(const std::vector °rees, - const std::map &dimensions); - - // Permute a given Eigen matrix. - static void permute_matrix(Eigen::MatrixXcd &matrix, - const std::vector &permutation); - - // Canonicalize degrees by sorting in descending order. - static std::vector canonicalize_degrees(const std::vector °rees); -}; +namespace detail { + +// Aggregate parameters from multiple mappings. +std::map aggregate_parameters( + const std::vector> ¶meter_mappings); + +// Extract documentation for a specific parameter from docstring. +std::string parameter_docs(const std::string ¶m_name, + const std::string &docs); + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> +args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args); + +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector generate_all_states(std::vector degrees, + std::map dimensions); + +// Permutes the given matrix according to the given permutation. +// If states is the current order of vector entries on which the given matrix +// acts, and permuted_states is the desired order of an array on which the +// permuted matrix should act, then the permutation is defined such that +// [states[i] for i in permutation] produces permuted_states. +cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation); + +// Returns the degrees sorted in canonical order. +std::vector canonicalize_degrees(std::vector degrees); +} // namespace detail } // namespace cudaq From 1e46c243fb4af94d10c6c2bf1a4f631f3ac40208 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 10:26:15 -0800 Subject: [PATCH 088/311] Adding equality comparision operation for matrix_2 Signed-off-by: Sachin Pisal --- runtime/cudaq/utils/tensor.cpp | 17 +++++++++++++++++ runtime/cudaq/utils/tensor.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index ce94678e57..5298067e9d 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -64,6 +64,23 @@ cudaq::matrix_2 &cudaq::matrix_2::operator-=(const cudaq::matrix_2 &right) { return *this; } +bool cudaq::operator==(const cudaq::matrix_2 &lhs, const cudaq::matrix_2 &rhs) { + if (lhs.get_rows() != rhs.get_rows() || + lhs.get_columns() != rhs.get_columns()) { + return false; + } + + for (std::size_t i = 0; i < lhs.get_rows(); i++) { + for (std::size_t j = 0; j < lhs.get_columns(); j++) { + if (lhs[{i, j}] != rhs[{i, j}]) { + return false; + } + } + } + + return true; +} + cudaq::matrix_2 & cudaq::matrix_2::kronecker_inplace(const cudaq::matrix_2 &right) { Dimensions new_dim{get_rows() * right.get_rows(), diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 5659b01d32..25518fa4a7 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -27,6 +27,8 @@ matrix_2 kronecker(const matrix_2 &, const matrix_2 &); template ::value_type> matrix_2 kronecker(Iterable begin, Iterable end); +// Equality comparison operator. +bool operator==(const matrix_2 &, const matrix_2 &); //===----------------------------------------------------------------------===// From ef3530abba53fdca73b444fd8be975092fa014ad Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 10:28:11 -0800 Subject: [PATCH 089/311] Fixing the helpers.h path Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 1 + runtime/cudaq/dynamics/manipulation.cpp | 2 +- runtime/cudaq/dynamics/operator_sum.cpp | 2 +- runtime/cudaq/dynamics/product_operators.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index b2cff5eab2..47301ad35f 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace cudaq { diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index e3d6f90447..e81c64ed84 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/helpers.h" #include "cudaq/operators.h" -#include "helpers.h" namespace cudaq { diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 4f37bd536e..90727ae9cf 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/helpers.h" #include "cudaq/operators.h" -#include "helpers.h" #include #include diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 80ea64f6df..0c50f14e95 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/helpers.h" #include "cudaq/operators.h" -#include "helpers.h" #include #include From b75d4a0c0615e8c0c458b147cd154e6d8670b606 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 14:32:35 -0800 Subject: [PATCH 090/311] Adding HandlerTy Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 2 +- runtime/cudaq/runge_kutta_integrator.h | 2 +- unittests/dynamics/test_cudm_helpers.cpp | 15 +++-- unittests/dynamics/test_helpers.cpp | 74 +++++++++++++----------- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index bbbbeeb001..1abe004c0d 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,7 +16,7 @@ #include namespace cudaq { -template > +template class BaseIntegrator { protected: std::map integrator_options; diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index aa45dc42ad..253337f267 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -15,7 +15,7 @@ #include namespace cudaq { -template > +template class runge_kutta_integrator : public BaseIntegrator { public: /// @brief Constructor to initialize the Runge-Kutta integrator diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 482108e128..7dce3168db 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,8 +12,7 @@ #include // Initialize operator_sum -template -cudaq::operator_sum initialize_operator_sum() { +cudaq::operator_sum initialize_operator_sum() { return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); } @@ -74,11 +73,11 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum>(); + auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator>( - handle, {}, op_sum, mode_extents); + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, + mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -89,9 +88,9 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum>(); + auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator>( - invalid_handle, {}, op_sum, mode_extents), + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, + op_sum, mode_extents), std::runtime_error); } diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp index aa14cb5c10..dab8d441f4 100644 --- a/unittests/dynamics/test_helpers.cpp +++ b/unittests/dynamics/test_helpers.cpp @@ -10,14 +10,14 @@ #include #include -using namespace cudaq; +using namespace cudaq::detail; TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { std::vector> mappings = { {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}}}; - auto result = OperatorHelpers::aggregate_parameters(mappings); + auto result = aggregate_parameters(mappings); EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); EXPECT_EQ(result["beta"], "Parameter B"); @@ -26,7 +26,7 @@ TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { std::vector> mappings; - auto result = OperatorHelpers::aggregate_parameters(mappings); + auto result = aggregate_parameters(mappings); EXPECT_TRUE(result.empty()); } @@ -37,10 +37,10 @@ TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { " alpha (float): The first parameter.\n" " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); + auto result = parameter_docs("alpha", docstring); EXPECT_EQ(result, "The first parameter."); - result = OperatorHelpers::parameter_docs("beta", docstring); + result = parameter_docs("beta", docstring); EXPECT_EQ(result, "The second parameter."); } @@ -50,13 +50,13 @@ TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { " alpha (float): The first parameter.\n" " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("gamma", docstring); + auto result = parameter_docs("gamma", docstring); EXPECT_EQ(result, ""); } TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { std::string docstring = ""; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); + auto result = parameter_docs("alpha", docstring); EXPECT_EQ(result, ""); } @@ -64,7 +64,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { std::vector degrees = {0, 1}; std::map dimensions = {{0, 2}, {1, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + auto states = generate_all_states(degrees, dimensions); std::vector expected_states = {"00", "01", "10", "11"}; EXPECT_EQ(states, expected_states); @@ -74,7 +74,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { std::vector degrees = {0, 1, 2}; std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + auto states = generate_all_states(degrees, dimensions); std::vector expected_states = {"000", "001", "010", "011", "100", "101", "110", "111"}; @@ -85,7 +85,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { std::vector degrees; std::map dimensions; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + auto states = generate_all_states(degrees, dimensions); EXPECT_TRUE(states.empty()); } @@ -93,55 +93,65 @@ TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { std::vector degrees = {0, 1, 2}; std::map dimensions = {{0, 2}, {1, 2}}; - EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), - std::out_of_range); + EXPECT_THROW(generate_all_states(degrees, dimensions), std::out_of_range); } TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { - Eigen::MatrixXcd matrix(2, 2); - matrix << 1, 2, 3, 4; + cudaq::matrix_2 matrix(2, 2); + matrix[{0, 0}] = 1; + matrix[{0, 1}] = 2; + matrix[{1, 0}] = 3; + matrix[{1, 1}] = 4; // Swap rows and columns std::vector permutation = {1, 0}; - OperatorHelpers::permute_matrix(matrix, permutation); + auto permuted_matrix = permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(2, 2); - expected << 4, 3, 2, 1; + cudaq::matrix_2 expected(2, 2); + matrix[{0, 0}] = 4; + matrix[{0, 1}] = 3; + matrix[{1, 0}] = 2; + matrix[{1, 1}] = 1; - EXPECT_EQ(matrix, expected); + EXPECT_EQ(permuted_matrix, expected); } TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { - Eigen::MatrixXcd matrix(3, 3); - matrix << 1, 2, 3, 4, 5, 6, 7, 8, 9; + cudaq::matrix_2 matrix(3, 3); + matrix[{0, 0}] = 1; + matrix[{0, 1}] = 2; + matrix[{0, 2}] = 3; + matrix[{1, 0}] = 4; + matrix[{1, 1}] = 5; + matrix[{1, 2}] = 6; + matrix[{2, 0}] = 7; + matrix[{2, 1}] = 8; + matrix[{2, 2}] = 9; // No change std::vector permutation = {0, 1, 2}; - OperatorHelpers::permute_matrix(matrix, permutation); + auto permuted_matrix = permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(3, 3); - expected << 1, 2, 3, 4, 5, 6, 7, 8, 9; - - EXPECT_EQ(matrix, expected); + EXPECT_EQ(permuted_matrix, matrix); } TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { std::vector degrees = {3, 1, 2}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + auto sorted = canonicalize_degrees(degrees); EXPECT_EQ(sorted, (std::vector{3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { std::vector degrees = {5, 4, 3, 2, 1}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + auto sorted = canonicalize_degrees(degrees); EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { std::vector degrees; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + auto sorted = canonicalize_degrees(degrees); EXPECT_TRUE(sorted.empty()); } @@ -152,8 +162,7 @@ TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { std::vector required_args = {"alpha", "beta"}; std::vector kwonly_args = {"gamma"}; - auto [args, kwonly] = - OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); + auto [args, kwonly] = args_from_kwargs(kwargs, required_args, kwonly_args); EXPECT_EQ(args.size(), 2); EXPECT_EQ(args[0], "0.5"); @@ -170,7 +179,6 @@ TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { std::vector required_args = {"alpha", "beta"}; std::vector kwonly_args = {"gamma"}; - EXPECT_THROW( - OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), - std::invalid_argument); + EXPECT_THROW(args_from_kwargs(kwargs, required_args, kwonly_args), + std::invalid_argument); } From 5dc3c9e8cce582e45317a84cae94d64996393921 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 15:56:30 -0800 Subject: [PATCH 091/311] Disabling unneeded (for now) variables Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 30 +++++++++---------- .../cudaq/dynamics/runge_kutta_integrator.cpp | 3 +- runtime/cudaq/runge_kutta_integrator.h | 6 ++-- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 1abe004c0d..fed0238ea5 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,17 +16,17 @@ #include namespace cudaq { -template +template class BaseIntegrator { protected: std::map integrator_options; TState state; double t; - std::map dimensions; - std::shared_ptr schedule; - std::shared_ptr> hamiltonian; + // std::map dimensions; + // std::shared_ptr schedule; + // std::shared_ptr> hamiltonian; std::shared_ptr> stepper; - std::vector>> collapse_operators; + // std::vector>> collapse_operators; virtual void post_init() = 0; @@ -61,16 +61,16 @@ class BaseIntegrator { } /// @brief Set the system parameters (dimensions, schedule, and operators) - void set_system(const std::map &dimensions, - std::shared_ptr schedule, - std::shared_ptr> hamiltonian, - std::vector>> - collapse_operators = {}) { - this->dimensions = dimensions; - this->schedule = schedule; - this->hamiltonian = hamiltonian; - this->collapse_operators = collapse_operators; - } + // void set_system(const std::map &dimensions, + // std::shared_ptr schedule, + // std::shared_ptr> hamiltonian, + // std::vector>> + // collapse_operators = {}) { + // this->dimensions = dimensions; + // this->schedule = schedule; + // this->hamiltonian = hamiltonian; + // this->collapse_operators = collapse_operators; + // } /// @brief Perform integration to the target time. virtual void integrate(double target_time) = 0; diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index d562f0ca26..87b7f48819 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -12,8 +12,7 @@ using namespace cudaq; namespace cudaq { -template -void runge_kutta_integrator::integrate(double target_time) { +void runge_kutta_integrator::integrate(double target_time) { if (!this->stepper) { throw std::runtime_error("Time stepper is not initialized."); } diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 253337f267..bd6b4445e3 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -15,8 +15,7 @@ #include namespace cudaq { -template -class runge_kutta_integrator : public BaseIntegrator { +class runge_kutta_integrator : public BaseIntegrator { public: /// @brief Constructor to initialize the Runge-Kutta integrator /// @param initial_state Initial quantum state. @@ -26,8 +25,7 @@ class runge_kutta_integrator : public BaseIntegrator { runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4) - : BaseIntegrator(std::move(initial_state), t0, - stepper), + : BaseIntegrator(std::move(initial_state), t0, stepper), substeps_(substeps) { if (!stepper) { throw std::invalid_argument("Time stepper must be initialized."); From 68188c73b3b8038ba50aa342e9277f9979a7e215 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 16:11:15 -0800 Subject: [PATCH 092/311] Adding cudaq::matrix_operator as a handler Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 5 ++- unittests/dynamics/test_cudm_helpers.cpp | 9 ++--- .../dynamics/test_runge_kutta_integrator.cpp | 34 +++++++------------ 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 50e83c5fdf..443020ff62 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -235,9 +235,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( return operator_handle; } catch (const std::exception &e) { - std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() - << std::endl; - throw; + throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); } } @@ -288,4 +286,5 @@ void destroy_array_gpu(void *gpu_array) { HANDLE_CUDA_ERROR(cudaFree(gpu_array)); } } + } // namespace cudaq diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 7dce3168db..15673729b7 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -76,8 +76,9 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, - mode_extents); + auto result = + cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -90,7 +91,7 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { std::vector mode_extents = {2, 2}; auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, - op_sum, mode_extents), + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + invalid_handle, {}, op_sum, mode_extents), std::runtime_error); } diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 3180bd3c2b..9541316100 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -19,7 +19,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::shared_ptr time_stepper_; - std::unique_ptr>> integrator_; + std::unique_ptr integrator_; std::unique_ptr state_; void SetUp() override { @@ -41,10 +41,8 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW( - integrator_ = - std::make_unique>>( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW(integrator_ = std::make_unique( + std::move(*state_), t0, time_stepper_, 4)); ASSERT_NE(integrator_, nullptr); } @@ -62,22 +60,18 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = - std::make_unique>>( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 1); + auto eulerIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 1); eulerIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); } // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = - std::make_unique>>( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto midpointIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); midpointIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } @@ -115,11 +109,9 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = - std::make_unique>>( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto integrator_missing_dt = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); } @@ -149,7 +141,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique>>( + EXPECT_THROW(std::make_unique( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 3), From 71b87c276faab6d910851b1fb7974adb0e0b02a4 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 16:25:09 -0800 Subject: [PATCH 093/311] Disabling template method Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 84 ++++++++++++++++++++++-- runtime/cudaq/dynamics/cudm_helpers.cpp | 75 --------------------- unittests/dynamics/test_cudm_helpers.cpp | 38 +++++------ 3 files changed, 97 insertions(+), 100 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 36a1ba9688..541ef77822 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -8,6 +8,7 @@ #pragma once +#include "cudaq/cudm_error_handling.h" #include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include @@ -25,12 +26,83 @@ compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents); -template -cudensitymatOperator_t convert_to_cudensitymat_operator( - cudensitymatHandle_t handle, - const std::map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents); +// std::map +// convert_dimensions(const std::vector &mode_extents) { +// std::map dimensions; +// for (size_t i = 0; i < mode_extents.size(); i++) { +// dimensions[static_cast(i)] = static_cast(mode_extents[i]); +// } +// return dimensions; +// } + +// template +// cudensitymatOperator_t convert_to_cudensitymat_operator( +// cudensitymatHandle_t handle, +// const std::map> ¶meters, +// const operator_sum &op, +// const std::vector &mode_extents) { +// if (op.get_terms().empty()) { +// throw std::invalid_argument("Operator sum cannot be empty."); +// } + +// try { +// cudensitymatOperator_t operator_handle; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( +// handle, static_cast(mode_extents.size()), +// mode_extents.data(), &operator_handle)); + +// std::vector elementary_operators; + +// for (const auto &product_op : op.get_terms()) { +// cudensitymatOperatorTerm_t term; + +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( +// handle, static_cast(mode_extents.size()), +// mode_extents.data(), &term)); + +// for (const auto &component : product_op.get_terms()) { +// if (std::holds_alternative(component)) { +// const auto &elem_op = std::get(component); + +// auto subspace_extents = +// get_subspace_extents(mode_extents, elem_op.degrees); +// auto flat_matrix = flatten_matrix( +// elem_op.to_matrix(convert_dimensions(mode_extents), +// parameters)); +// auto cudm_elem_op = +// create_elementary_operator(handle, subspace_extents, +// flat_matrix); + +// elementary_operators.push_back(cudm_elem_op); +// append_elementary_operator_to_term(handle, term, cudm_elem_op, +// elem_op.degrees); +// } else if (std::holds_alternative(component)) +// { +// auto coeff = +// std::get(component).evaluate(parameters); +// append_scalar_to_term(handle, term, coeff); +// } +// } + +// // Append the product operator term to the top-level operator +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), +// {nullptr, nullptr})); + +// // Destroy the term +// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + +// // Cleanup +// for (auto &elem_op : elementary_operators) { +// HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); +// } +// } + +// return operator_handle; +// } catch (const std::exception &e) { +// throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); +// } +// } cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 443020ff62..8e59a2f130 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -164,81 +164,6 @@ compute_lindblad_operator(cudensitymatHandle_t handle, return lindblad_op; } -std::map -convert_dimensions(const std::vector &mode_extents) { - std::map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } - return dimensions; -} - -template -cudensitymatOperator_t convert_to_cudensitymat_operator( - cudensitymatHandle_t handle, - const std::map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents) { - if (op.get_terms().empty()) { - throw std::invalid_argument("Operator sum cannot be empty."); - } - - try { - cudensitymatOperator_t operator_handle; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &operator_handle)); - - std::vector elementary_operators; - - for (const auto &product_op : op.get_terms()) { - cudensitymatOperatorTerm_t term; - - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term)); - - for (const auto &component : product_op.get_terms()) { - if (std::holds_alternative(component)) { - const auto &elem_op = std::get(component); - - auto subspace_extents = - get_subspace_extents(mode_extents, elem_op.degrees); - auto flat_matrix = flatten_matrix( - elem_op.to_matrix(convert_dimensions(mode_extents), parameters)); - auto cudm_elem_op = - create_elementary_operator(handle, subspace_extents, flat_matrix); - - elementary_operators.push_back(cudm_elem_op); - append_elementary_operator_to_term(handle, term, cudm_elem_op, - elem_op.degrees); - } else if (std::holds_alternative(component)) { - auto coeff = - std::get(component).evaluate(parameters); - append_scalar_to_term(handle, term, coeff); - } - } - - // Append the product operator term to the top-level operator - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), - {nullptr, nullptr})); - - // Destroy the term - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - - // Cleanup - for (auto &elem_op : elementary_operators) { - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - } - } - - return operator_handle; - } catch (const std::exception &e) { - throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); - } -} - cudensitymatOperator_t construct_liouvillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 15673729b7..04ee031fd1 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -70,28 +70,28 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { } // Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { - std::vector mode_extents = {2, 2}; +// TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { +// std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); +// auto op_sum = initialize_operator_sum(); - EXPECT_NO_THROW({ - auto result = - cudaq::convert_to_cudensitymat_operator( - handle, {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); - }); -} +// EXPECT_NO_THROW({ +// auto result = +// cudaq::convert_to_cudensitymat_operator( +// handle, {}, op_sum, mode_extents); +// ASSERT_NE(result, nullptr); +// cudensitymatDestroyOperator(result); +// }); +// } // Test invalid handle -TEST_F(CuDensityMatTestFixture, InvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; +// TEST_F(CuDensityMatTestFixture, InvalidHandle) { +// cudensitymatHandle_t invalid_handle = nullptr; - std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); +// std::vector mode_extents = {2, 2}; +// auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( - invalid_handle, {}, op_sum, mode_extents), - std::runtime_error); -} +// EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( +// invalid_handle, {}, op_sum, mode_extents), +// std::runtime_error); +// } From eb7fdc55c39f6d3ae1c37ea99089597c7513c1c8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 17:01:32 -0800 Subject: [PATCH 094/311] Fixing template definition in the header and instatiating it in teh cpp file Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 89 +++---------------------- runtime/cudaq/dynamics/cudm_helpers.cpp | 80 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 78 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 541ef77822..ede4ee0243 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -8,7 +8,6 @@ #pragma once -#include "cudaq/cudm_error_handling.h" #include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include @@ -26,83 +25,12 @@ compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents); -// std::map -// convert_dimensions(const std::vector &mode_extents) { -// std::map dimensions; -// for (size_t i = 0; i < mode_extents.size(); i++) { -// dimensions[static_cast(i)] = static_cast(mode_extents[i]); -// } -// return dimensions; -// } - -// template -// cudensitymatOperator_t convert_to_cudensitymat_operator( -// cudensitymatHandle_t handle, -// const std::map> ¶meters, -// const operator_sum &op, -// const std::vector &mode_extents) { -// if (op.get_terms().empty()) { -// throw std::invalid_argument("Operator sum cannot be empty."); -// } - -// try { -// cudensitymatOperator_t operator_handle; -// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( -// handle, static_cast(mode_extents.size()), -// mode_extents.data(), &operator_handle)); - -// std::vector elementary_operators; - -// for (const auto &product_op : op.get_terms()) { -// cudensitymatOperatorTerm_t term; - -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( -// handle, static_cast(mode_extents.size()), -// mode_extents.data(), &term)); - -// for (const auto &component : product_op.get_terms()) { -// if (std::holds_alternative(component)) { -// const auto &elem_op = std::get(component); - -// auto subspace_extents = -// get_subspace_extents(mode_extents, elem_op.degrees); -// auto flat_matrix = flatten_matrix( -// elem_op.to_matrix(convert_dimensions(mode_extents), -// parameters)); -// auto cudm_elem_op = -// create_elementary_operator(handle, subspace_extents, -// flat_matrix); - -// elementary_operators.push_back(cudm_elem_op); -// append_elementary_operator_to_term(handle, term, cudm_elem_op, -// elem_op.degrees); -// } else if (std::holds_alternative(component)) -// { -// auto coeff = -// std::get(component).evaluate(parameters); -// append_scalar_to_term(handle, term, coeff); -// } -// } - -// // Append the product operator term to the top-level operator -// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( -// handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), -// {nullptr, nullptr})); - -// // Destroy the term -// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - -// // Cleanup -// for (auto &elem_op : elementary_operators) { -// HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); -// } -// } - -// return operator_handle; -// } catch (const std::exception &e) { -// throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); -// } -// } +template +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map> ¶meters, + const operator_sum &op, + const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, @@ -114,4 +42,9 @@ void *create_array_gpu(const std::vector> &cpu_array); // Function to detsroy a previously created array copy in GPU memory void destroy_array_gpu(void *gpu_array); + +extern template cudensitymatOperator_t +convert_to_cudensitymat_operator( + cudensitymatHandle_t, const std::map> &, + const operator_sum &, const std::vector &); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 8e59a2f130..a96ddd6465 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -164,6 +164,81 @@ compute_lindblad_operator(cudensitymatHandle_t handle, return lindblad_op; } +std::map +convert_dimensions(const std::vector &mode_extents) { + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } + return dimensions; +} + +template +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map> ¶meters, + const operator_sum &op, + const std::vector &mode_extents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + + try { + cudensitymatOperator_t operator_handle; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &operator_handle)); + + std::vector elementary_operators; + + for (const auto &product_op : op.get_terms()) { + cudensitymatOperatorTerm_t term; + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term)); + + for (const auto &component : product_op.get_terms()) { + if (auto elem_op = + dynamic_cast(&component)) { + auto subspace_extents = + get_subspace_extents(mode_extents, elem_op->degrees); + auto flat_matrix = flatten_matrix( + elem_op->to_matrix(convert_dimensions(mode_extents), parameters)); + auto cudm_elem_op = + create_elementary_operator(handle, subspace_extents, flat_matrix); + + elementary_operators.push_back(cudm_elem_op); + append_elementary_operator_to_term(handle, term, cudm_elem_op, + elem_op->degrees); + // } else if (auto scalar_op = static_cast(&component)) { + // auto coeff = + // scalar_op->evaluate(parameters); + // append_scalar_to_term(handle, term, coeff); + } + } + + // Append the product operator term to the top-level operator + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); + + // Destroy the term + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + + // Cleanup + for (auto &elem_op : elementary_operators) { + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + } + } + + return operator_handle; + } catch (const std::exception &e) { + throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); + } +} + cudensitymatOperator_t construct_liouvillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, @@ -212,4 +287,9 @@ void destroy_array_gpu(void *gpu_array) { } } +template cudensitymatOperator_t +convert_to_cudensitymat_operator( + cudensitymatHandle_t, const std::map> &, + const operator_sum &, const std::vector &); + } // namespace cudaq From 37a0ee376b552448a96f0faf39015c2e0dfca3d5 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 17:03:54 -0800 Subject: [PATCH 095/311] Enabling tests Signed-off-by: Sachin Pisal --- unittests/dynamics/test_cudm_helpers.cpp | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 04ee031fd1..15673729b7 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -70,28 +70,28 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { } // Test for convert_to_cudensitymat_operator -// TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { -// std::vector mode_extents = {2, 2}; +TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { + std::vector mode_extents = {2, 2}; -// auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); -// EXPECT_NO_THROW({ -// auto result = -// cudaq::convert_to_cudensitymat_operator( -// handle, {}, op_sum, mode_extents); -// ASSERT_NE(result, nullptr); -// cudensitymatDestroyOperator(result); -// }); -// } + EXPECT_NO_THROW({ + auto result = + cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); + cudensitymatDestroyOperator(result); + }); +} // Test invalid handle -// TEST_F(CuDensityMatTestFixture, InvalidHandle) { -// cudensitymatHandle_t invalid_handle = nullptr; +TEST_F(CuDensityMatTestFixture, InvalidHandle) { + cudensitymatHandle_t invalid_handle = nullptr; -// std::vector mode_extents = {2, 2}; -// auto op_sum = initialize_operator_sum(); + std::vector mode_extents = {2, 2}; + auto op_sum = initialize_operator_sum(); -// EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( -// invalid_handle, {}, op_sum, mode_extents), -// std::runtime_error); -// } + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + invalid_handle, {}, op_sum, mode_extents), + std::runtime_error); +} From 02d43622ac4403bbe55d6e5085c26ff9f1cef7df Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 17:43:34 -0800 Subject: [PATCH 096/311] Fixing Rydberg and renaming fixture in test_cudm_helpers Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 1 + .../cudaq/dynamics/rydberg_hamiltonian.cpp | 24 +++------ runtime/cudaq/operators.h | 3 +- unittests/dynamics/rydberg_hamiltonian.cpp | 53 +++++++++---------- unittests/dynamics/test_cudm_helpers.cpp | 12 ++--- 5 files changed, 40 insertions(+), 53 deletions(-) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 170c7b963d..241fab28c9 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -25,6 +25,7 @@ set(CUDAQ_OPS_SRC schedule.cpp manipulation.cpp helpers.cpp + rydberg_hamiltonian.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp index 668c9a23db..3d8b125ad3 100644 --- a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp +++ b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp @@ -11,8 +11,7 @@ #include namespace cudaq { -template -rydberg_hamiltonian::rydberg_hamiltonian( +rydberg_hamiltonian::rydberg_hamiltonian( const std::vector &atom_sites, const scalar_operator &litude, const scalar_operator &phase, const scalar_operator &delta_global, const std::vector &atom_filling, @@ -35,31 +34,22 @@ rydberg_hamiltonian::rydberg_hamiltonian( } } -template -const std::vector::Coordinate> & -rydberg_hamiltonian::get_atom_sites() const { +const std::vector & +rydberg_hamiltonian::get_atom_sites() const { return atom_sites; } -template -const std::vector & -rydberg_hamiltonian::get_atom_filling() const { +const std::vector &rydberg_hamiltonian::get_atom_filling() const { return atom_filling; } -template -const scalar_operator &rydberg_hamiltonian::get_amplitude() const { +const scalar_operator &rydberg_hamiltonian::get_amplitude() const { return amplitude; } -template -const scalar_operator &rydberg_hamiltonian::get_phase() const { - return phase; -} +const scalar_operator &rydberg_hamiltonian::get_phase() const { return phase; } -template -const scalar_operator & -rydberg_hamiltonian::get_delta_global() const { +const scalar_operator &rydberg_hamiltonian::get_delta_global() const { return delta_global; } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 2a9281ace9..47582c8a11 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -629,8 +629,7 @@ class matrix_operator { }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system -template -class rydberg_hamiltonian : public operator_sum { +class rydberg_hamiltonian { public: using Coordinate = std::pair; diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp index 33e7fdff71..f3883366ba 100644 --- a/unittests/dynamics/rydberg_hamiltonian.cpp +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ #include "cudaq/operators.h" @@ -13,8 +13,8 @@ using namespace cudaq; TEST(RydbergHamiltonianTest, ConstructorValidInputs) { // Valid atom sites - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -22,8 +22,7 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { scalar_operator delta_global(-0.5); // Valid atom filling - rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, - phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); @@ -36,8 +35,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { } TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -47,16 +46,16 @@ TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { // Valid atom filling std::vector atom_filling = {1, 0, 1}; - rydberg_hamiltonian> hamiltonian( - atom_sites, amplitude, phase, delta_global, atom_filling); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); } TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -66,14 +65,14 @@ TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { // Invalid atom filling size std::vector atom_filling = {1, 0}; - EXPECT_THROW(rydberg_hamiltonian>( - atom_sites, amplitude, phase, delta_global, atom_filling), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling), std::invalid_argument); } TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -84,22 +83,21 @@ TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { auto delta_local = std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); - EXPECT_THROW(rydberg_hamiltonian>( - atom_sites, amplitude, phase, delta_global, {}, delta_local), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + {}, delta_local), std::runtime_error); } TEST(RydbergHamiltonianTest, Accessors) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, - phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), @@ -111,16 +109,15 @@ TEST(RydbergHamiltonianTest, Accessors) { } TEST(RydbergHamiltonianTest, DefaultAtomFilling) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, - phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); std::vector expected_filling(atom_sites.size(), 1); EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 15673729b7..24ec623f8f 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -16,7 +16,7 @@ cudaq::operator_sum initialize_operator_sum() { return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); } -class CuDensityMatTestFixture : public ::testing::Test { +class CuDensityMatHelpersTestFixture : public ::testing::Test { protected: cudensitymatHandle_t handle; cudaStream_t stream; @@ -30,7 +30,7 @@ class CuDensityMatTestFixture : public ::testing::Test { }; // Test for initialize_state -TEST_F(CuDensityMatTestFixture, InitializeState) { +TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { std::vector mode_extents = {2}; std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; @@ -41,7 +41,7 @@ TEST_F(CuDensityMatTestFixture, InitializeState) { } // Test for scale_state -TEST_F(CuDensityMatTestFixture, ScaleState) { +TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { std::vector mode_extents = {2}; std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; @@ -54,7 +54,7 @@ TEST_F(CuDensityMatTestFixture, ScaleState) { } // Test for compute_lindblad_op -TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { +TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { std::vector mode_extents = {2, 2}; cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); @@ -70,7 +70,7 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { } // Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { +TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; auto op_sum = initialize_operator_sum(); @@ -85,7 +85,7 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { } // Test invalid handle -TEST_F(CuDensityMatTestFixture, InvalidHandle) { +TEST_F(CuDensityMatHelpersTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; From 34b40bee28ca3dc19e25e1f0c5de4dd9ced004e1 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 18:28:22 -0800 Subject: [PATCH 097/311] * Enabling dynamic_cast for scalar operator * Fixing degrees in unittest when creating operator_sum Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 25 +++++++++++++++++------- runtime/cudaq/operators.h | 2 +- unittests/dynamics/test_cudm_helpers.cpp | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index a96ddd6465..667c53bf52 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -85,6 +85,10 @@ void append_elementary_operator_to_term( throw std::invalid_argument("Degrees vector cannot be empty."); } + if (!elem_op) { + throw std::invalid_argument("elem_op cannot be null."); + } + std::vector elem_ops = {elem_op}; std::vector modeActionDuality(degrees.size(), 0); @@ -147,6 +151,11 @@ compute_lindblad_operator(cudensitymatHandle_t handle, cudensitymatElementaryOperator_t cudm_elem_op = create_elementary_operator(handle, mode_extents, flat_matrix); + if (!cudm_elem_op) { + throw std::runtime_error( + "Failed to create elementary operator in compute_lindblad_operator."); + } + // Append the elementary operator to the term std::vector degrees = {0, 1}; append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); @@ -199,7 +208,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( mode_extents.data(), &term)); for (const auto &component : product_op.get_terms()) { - if (auto elem_op = + if (const auto *elem_op = dynamic_cast(&component)) { auto subspace_extents = get_subspace_extents(mode_extents, elem_op->degrees); @@ -211,11 +220,11 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( elementary_operators.push_back(cudm_elem_op); append_elementary_operator_to_term(handle, term, cudm_elem_op, elem_op->degrees); - // } else if (auto scalar_op = static_cast(&component)) { - // auto coeff = - // scalar_op->evaluate(parameters); - // append_scalar_to_term(handle, term, coeff); + } else if (const auto *scalar_op = + dynamic_cast( + &component)) { + auto coeff = scalar_op->evaluate(parameters); + append_scalar_to_term(handle, term, coeff); } } @@ -235,7 +244,9 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( return operator_handle; } catch (const std::exception &e) { - throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); + std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() + << std::endl; + throw; } } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 47582c8a11..d818c55d68 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -551,7 +551,7 @@ class matrix_operator { return *this; } - ~matrix_operator() = default; + virtual ~matrix_operator() = default; /// @brief The degrees of freedom that the operator acts on in canonical /// order. diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 24ec623f8f..c5d5e9759c 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -13,7 +13,7 @@ // Initialize operator_sum cudaq::operator_sum initialize_operator_sum() { - return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + return cudaq::matrix_operator::create(0) + cudaq::matrix_operator::create(1); } class CuDensityMatHelpersTestFixture : public ::testing::Test { From bbd743446c645b58d8419aa48d2aeef977570d72 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 6 Feb 2025 02:46:38 +0000 Subject: [PATCH 098/311] Minor cmake restructure Signed-off-by: Thien Nguyen --- CMakeLists.txt | 3 +++ unittests/CMakeLists.txt | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b9a0acaaa..1a44cf7494 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,9 @@ endif() if(NOT CUTENSORNET_ROOT) SET(CUTENSORNET_ROOT "$ENV{CUQUANTUM_INSTALL_PREFIX}") endif() +if(NOT CUDENSITYMAT_ROOT) + SET(CUDENSITYMAT_ROOT "$ENV{CUQUANTUM_INSTALL_PREFIX}") +endif() if(NOT CUTENSOR_ROOT) SET(CUTENSOR_ROOT "$ENV{CUTENSOR_INSTALL_PREFIX}") endif() diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 7b48ef7289..a5db4a6718 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -44,12 +44,6 @@ set(CUDAQ_RUNTIME_TEST_SOURCES common/NoiseModelTester.cpp integration/tracer_tester.cpp integration/gate_library_tester.cpp - dynamics/test_runge_kutta_integrator.cpp - dynamics/test_helpers.cpp - dynamics/rydberg_hamiltonian.cpp - dynamics/test_cudm_helpers.cpp - dynamics/test_cudm_state.cpp - dynamics/test_cudm_time_stepper.cpp ) # Make it so we can get function symbols @@ -81,9 +75,7 @@ macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) fmt::fmt-header-only cudaq-platform-default cudaq-builder - gtest_main - $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 - /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) + gtest_main) set(TEST_LABELS "") if (${NVQIR_BACKEND} STREQUAL "qpp") target_compile_definitions(${TEST_EXE_NAME} PRIVATE -DCUDAQ_SIMULATION_SCALAR_FP64) @@ -289,11 +281,36 @@ target_link_libraries(test_operators cudaq-operators cudaq gtest_main - $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 - /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12 fmt::fmt-header-only) gtest_discover_tests(test_operators) +if (CUDA_FOUND) + find_package(CUDAToolkit REQUIRED) + + # Create an executable for dynamics UnitTests + set(CUDAQ_DYNAMICS_TEST_SOURCES + dynamics/test_runge_kutta_integrator.cpp + dynamics/test_helpers.cpp + dynamics/rydberg_hamiltonian.cpp + dynamics/test_cudm_helpers.cpp + dynamics/test_cudm_state.cpp + dynamics/test_cudm_time_stepper.cpp + ) + add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_options(test_dynamics PRIVATE -Wl,--no-as-needed) + endif() + target_link_libraries(test_dynamics + PRIVATE + cudaq-spin + cudaq-operators + cudaq + ${CUDENSITYMAT_ROOT}/lib/libcudensitymat.so.0 + CUDA::cudart_static + gtest_main + fmt::fmt-header-only) + gtest_discover_tests(test_dynamics) +endif() add_subdirectory(plugin) # build the test qudit execution manager From bd67453b592f312947c39b96d700e4dd660fc37e Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 13:17:57 -0800 Subject: [PATCH 099/311] Adding cudm_op_conversion interface Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 runtime/cudaq/cudm_op_conversion.h diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h new file mode 100644 index 0000000000..73e927515e --- /dev/null +++ b/runtime/cudaq/cudm_op_conversion.h @@ -0,0 +1,61 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/cudm_helpers.h" +#include "cudaq/operators.h" +#include "cudaq/schedule.h" +#include +#include + +namespace cudaq { +class cudm_op_conversion { +public: + cudm_op_conversion(const cudensitymatHandle_t handle, + const std::map &dimensions, + const std::shared_ptr schedule = nullptr); + + // Tensor product of two operator terms + cudensitymatOperatorTerm_t tensor(const cudensitymatOperatorTerm_t &op1, + const cudensitymatOperatorTerm_t &op2); + + // Multiplication of two operator terms + cudensitymatOperatorTerm_t mul(const cudensitymatOperatorTerm_t &op1, + const cudensitymatOperatorTerm_t &op2); + + // Addition of two operator terms + cudensitymatOperatorTerm_t add(const cudensitymatOperatorTerm_t &op1, + const cudensitymatOperatorTerm_t &op2); + + // Evaluate an operator and convert it to cudensitymatOperatorTerm_t + cudensitymatOperatorTerm_t evaluate(const matrix_operator &op); + + // Convert a scalar to a cudensitymat operator term + cudensitymatOperatorTerm_t _scalar_to_op(const scalar_operator &scalar); + + // Multiplies a scalar callback with a cudensitymat operator term + cudensitymatOperatorTerm_t + _callback_mult_op(cudensitymatScalarCallback_t scalar, + cudensitymatOperatorTerm_t op); + + // Wrap a matrix operator as a cudensitymat tensor callback + cudensitymatOperatorTerm_t _wrap_callback_tensor(const matrix_operator &op); + +private: + std::map dimensions_; + std::shared_ptr schedule_; + cudensitymatHandle_t handle_; + + std::map> + _termtoElemOps; + std::map> _termtoModes; + std::map> _termtoDuals; +}; +} // namespace cudaq \ No newline at end of file From a086a3385af5042c520b7fbd0ea5858c8a07946b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 13:38:46 -0800 Subject: [PATCH 100/311] Fixing the return type of _wrap_callback_tensor Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 73e927515e..8217e75412 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -45,7 +45,7 @@ class cudm_op_conversion { cudensitymatOperatorTerm_t op); // Wrap a matrix operator as a cudensitymat tensor callback - cudensitymatOperatorTerm_t _wrap_callback_tensor(const matrix_operator &op); + cudensitymatTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); private: std::map dimensions_; From 8883ea730126a52dd810cde9b0d12d7f0f2f4c5a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 14:28:17 -0800 Subject: [PATCH 101/311] Updating cudm_op_conversion interface to accommodate wrap callback and std::variant Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 55 +++++++++++++++++------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 8217e75412..462a8eb477 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -22,40 +22,47 @@ class cudm_op_conversion { const std::shared_ptr schedule = nullptr); // Tensor product of two operator terms - cudensitymatOperatorTerm_t tensor(const cudensitymatOperatorTerm_t &op1, - const cudensitymatOperatorTerm_t &op2); + std::variant + tensor(const std::variant &op1, + const std::variant &op2); // Multiplication of two operator terms - cudensitymatOperatorTerm_t mul(const cudensitymatOperatorTerm_t &op1, - const cudensitymatOperatorTerm_t &op2); + std::variant + mul(const std::variant &op1, + const std::variant &op2); // Addition of two operator terms - cudensitymatOperatorTerm_t add(const cudensitymatOperatorTerm_t &op1, - const cudensitymatOperatorTerm_t &op2); + std::variant + add(const std::variant &op1, + const std::variant &op2); // Evaluate an operator and convert it to cudensitymatOperatorTerm_t - cudensitymatOperatorTerm_t evaluate(const matrix_operator &op); - - // Convert a scalar to a cudensitymat operator term - cudensitymatOperatorTerm_t _scalar_to_op(const scalar_operator &scalar); - - // Multiplies a scalar callback with a cudensitymat operator term - cudensitymatOperatorTerm_t - _callback_mult_op(cudensitymatScalarCallback_t scalar, - cudensitymatOperatorTerm_t op); - - // Wrap a matrix operator as a cudensitymat tensor callback - cudensitymatTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); + std::variant + evaluate(const std::variant> &op); private: + cudensitymatHandle_t handle_; std::map dimensions_; std::shared_ptr schedule_; - cudensitymatHandle_t handle_; - std::map> - _termtoElemOps; - std::map> _termtoModes; - std::map> _termtoDuals; + cudensitymatOperatorTerm_t + _callback_mult_op(const cudensitymatWrappedScalarCallback_t &scalar, + const cudensitymatOperatorTerm_t &op); + cudensitymatOperatorTerm_t + _scalar_to_op(const cudensitymatWrappedScalarCallback_t &scalar); + cudensitymatWrappedTensorCallback_t _wrap_callback(const scalar_operator &op); + cudensitymatWrappedTensorCallback_t + _wrap_callback_tensor(const matrix_operator &op); }; } // namespace cudaq \ No newline at end of file From 4b20e972220d9ec1b8af8f90fb19e05086e8a90f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 17:43:24 -0800 Subject: [PATCH 102/311] Adding cudm_op_conversion implementation, exposing get_generator for the scalar_operator Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 6 +- runtime/cudaq/dynamics/CMakeLists.txt | 1 + runtime/cudaq/dynamics/cudm_op_conversion.cpp | 286 ++++++++++++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 4 + runtime/cudaq/operators.h | 2 + 5 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 runtime/cudaq/dynamics/cudm_op_conversion.cpp diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 462a8eb477..50395f0c56 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -47,9 +47,9 @@ class cudm_op_conversion { // Evaluate an operator and convert it to cudensitymatOperatorTerm_t std::variant + std::complex> evaluate(const std::variant> &op); + product_operator> &op); private: cudensitymatHandle_t handle_; @@ -61,7 +61,7 @@ class cudm_op_conversion { const cudensitymatOperatorTerm_t &op); cudensitymatOperatorTerm_t _scalar_to_op(const cudensitymatWrappedScalarCallback_t &scalar); - cudensitymatWrappedTensorCallback_t _wrap_callback(const scalar_operator &op); + cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &op); cudensitymatWrappedTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); }; diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 241fab28c9..95d56934f1 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -26,6 +26,7 @@ set(CUDAQ_OPS_SRC manipulation.cpp helpers.cpp rydberg_hamiltonian.cpp + cudm_op_conversion.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp new file mode 100644 index 0000000000..b16f4231f4 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -0,0 +1,286 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_op_conversion.h" +#include "cudaq/cudm_error_handling.h" +#include +#include +#include + +using namespace cudaq; + +namespace cudaq { +cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, + const std::map &dimensions, + std::shared_ptr schedule) + : handle_(handle), dimensions_(dimensions), schedule_(schedule) {} + +cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( + const cudensitymatWrappedScalarCallback_t &scalar) { + cudensitymatOperatorTerm_t op_term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &op_term)); + + cudensitymatElementaryOperator_t identity; + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle_, 1, nullptr, CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, + CUDA_C_64F, nullptr, {nullptr, nullptr}, &identity)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle_, op_term, 1, &identity, nullptr, nullptr, {1.0, 0.0}, scalar)); + + return op_term; +} + +cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( + const cudensitymatWrappedScalarCallback_t &scalar, + const cudensitymatOperatorTerm_t &op) { + if (!op) { + throw std::invalid_argument("Invalid operator term (nullptr)."); + } + + cudensitymatOperatorTerm_t new_opterm; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &new_opterm)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_opterm, op, 0, + {1.0, 0.0}, scalar)); + + return new_opterm; +} + +std::variant +cudm_op_conversion::tensor( + const std::variant &op1, + const std::variant &op2) { + if (std::holds_alternative(op1) || + std::holds_alternative(op2)) { + return std::get(op1) * std::get(op2); + } + + cudensitymatOperatorTerm_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &result)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op1), 0, {1.0, 0.0}, + {nullptr, nullptr})); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op2), 0, {1.0, 0.0}, + {nullptr, nullptr})); + + return result; +} + +std::variant +cudm_op_conversion::mul( + const std::variant &op1, + const std::variant &op2) { + return tensor(op1, op2); +} + +std::variant +cudm_op_conversion::add( + const std::variant &op1, + const std::variant &op2) { + if (std::holds_alternative(op1) || + std::holds_alternative(op2)) { + return std::get(op1) + std::get(op2); + } + + cudensitymatOperatorTerm_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &result)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op1), 0, {1.0, 0.0}, + {nullptr, nullptr})); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op2), 0, {1.0, 0.0}, + {nullptr, nullptr})); + + return result; +} + +std::variant> +cudm_op_conversion::evaluate( + const std::variant> &op) { + if (std::holds_alternative(op)) { + const scalar_operator &scalar_op = std::get(op); + + ScalarCallbackFunction generator = scalar_op.get_generator(); + + if (!generator) { + return scalar_op.evaluate({}); + } else { + return _wrap_callback(scalar_op); + } + } + + if (std::holds_alternative(op)) { + const matrix_operator &mat_op = std::get(op); + + cudensitymatOperatorTerm_t opterm; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle_, dimensions_.size(), nullptr, &opterm)); + + cudensitymatElementaryOperator_t elem_op; + cudensitymatWrappedTensorCallback_t callback = + _wrap_callback_tensor(mat_op); + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle_, mat_op.degrees.size(), nullptr, + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, nullptr, + callback, &elem_op)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle_, opterm, 1, &elem_op, mat_op.degrees.data(), nullptr, + {1.0, 0.0}, {nullptr, nullptr})); + + return opterm; + } + + if (std::holds_alternative>(op)) { + throw std::runtime_error( + "Handling of product_operator is not implemented."); + } + + throw std::runtime_error( + "Unknown operator type in cudm_op_conversion::evaluate."); +} + +cudensitymatWrappedScalarCallback_t +cudm_op_conversion::_wrap_callback(const scalar_operator &scalar_op) { + try { + std::complex evaluatedValue = scalar_op.evaluate({}); + + cudensitymatWrappedScalarCallback_t wrapped_callback; + wrapped_callback.callback = nullptr; + wrapped_callback.wrapper = new std::complex(evaluatedValue); + return wrapped_callback; + } catch (const std::exception &) { + } + + ScalarCallbackFunction generator = scalar_op.get_generator(); + + if (!generator) { + throw std::runtime_error( + "scalar_operator does not have a valid generator function."); + } + + auto callback = [](double time, int32_t num_params, const double params[], + cudaDataType_t data_type, + void *scalar_storage) -> int32_t { + try { + scalar_operator *scalar_op = + static_cast(scalar_storage); + + std::map> param_map; + for (size_t i = 0; i < num_params; i++) { + param_map[std::to_string(i)] = params[i]; + } + + std::complex result = scalar_op->evaluate(param_map); + + if (data_type == CUDA_C_64F) { + *reinterpret_cast(scalar_storage) = + make_cuDoubleComplex(result.real(), result.imag()); + } else if (data_type == CUDA_C_32F) { + *reinterpret_cast(scalar_storage) = + make_cuFloatComplex(static_cast(result.real()), + static_cast(result.imag())); + } else { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in scalar callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedScalarCallback_t wrappedCallback; + wrappedCallback.callback = callback; + wrappedCallback.wrapper = new scalar_operator(scalar_op); + + return wrappedCallback; +} + +cudensitymatWrappedTensorCallback_t +cudm_op_conversion::_wrap_callback_tensor(const matrix_operator &op) { + auto callback = + [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, + const int64_t mode_extents[], const int32_t diagonal_offsets[], + double time, int32_t num_params, const double params[], + cudaDataType_t data_type, void *tensor_storage) -> int32_t { + try { + matrix_operator *mat_op = static_cast(tensor_storage); + + std::map> param_map; + for (size_t i = 0; i < num_params; i++) { + param_map[std::to_string(i)] = params[i]; + } + + matrix_2 matrix_data = mat_op->to_matrix({}, param_map); + + std::size_t rows = matrix_data.get_rows(); + std::size_t cols = matrix_data.get_columns(); + + if (num_modes != rows) { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + if (data_type == CUDA_C_64F) { + cuDoubleComplex *storage = + static_cast(tensor_storage); + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + storage[i * cols + j] = make_cuDoubleComplex( + matrix_data[{i, j}].real(), matrix_data[{i, j}].imag()); + } + } + } else if (data_type == CUDA_C_32F) { + cuFloatComplex *storage = static_cast(tensor_storage); + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + storage[i * cols + j] = make_cuFloatComplex( + static_cast(matrix_data[{i, j}].real()), + static_cast(matrix_data[{i, j}].imag())); + } + } + } else { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in tensor callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedTensorCallback_t wrapped_callback; + wrapped_callback.callback = callback; + wrapped_callback.wrapper = new matrix_operator(op); + + return wrapped_callback; +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 5371926a07..a7f2f052c9 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -65,6 +65,10 @@ std::complex scalar_operator::evaluate( return generator(parameters); } +ScalarCallbackFunction scalar_operator::get_generator() const { + return generator; +} + matrix_2 scalar_operator::to_matrix( const std::map dimensions, const std::map> parameters) const { diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d818c55d68..baa9e7bee6 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -73,6 +73,8 @@ class scalar_operator { std::complex evaluate( const std::map> parameters = {}) const; + ScalarCallbackFunction get_generator() const; + // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. matrix_2 to_matrix( From 5e9c058803b4085b72afd89e46929a6f64a15c51 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 7 Feb 2025 02:08:42 +0000 Subject: [PATCH 103/311] Fix segfault and add an end-to-end stepper check Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_state.h | 4 ++ runtime/cudaq/dynamics/cudm_helpers.cpp | 37 +++++++++---------- runtime/cudaq/dynamics/cudm_state.cpp | 2 + unittests/dynamics/test_cudm_time_stepper.cpp | 25 +++++++++++++ 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 486e3034ac..227dcb63e4 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -72,6 +72,10 @@ class cudm_state { /// @return A copy of the raw data as a vector of complex numbers. std::vector> get_raw_data() const; + /// @brief Get the pointer to device memory buffer storing the state. + /// @return GPU device pointer + void *get_device_pointer() const; + /// @brief Get a copy of the hilbert space dimensions for the quantum state. /// @return A copy of the hilbert space dimensions of a vector of integers. std::vector get_hilbert_space_dims() const; diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 667c53bf52..5d8ab4d0c2 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -10,13 +10,13 @@ #include "cudaq/cudm_error_handling.h" namespace cudaq { -// Function to flatten a matrix into a 1D array +// Function to flatten a matrix into a 1D array (column major) std::vector> flatten_matrix(const matrix_2 &matrix) { std::vector> flat_matrix; - - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); + flat_matrix.reserve(matrix.get_size()); + for (size_t col = 0; col < matrix.get_columns(); col++) { + for (size_t row = 0; row < matrix.get_rows(); row++) { + flat_matrix.push_back(matrix[{row, col}]); } } @@ -53,19 +53,14 @@ cudensitymatElementaryOperator_t create_elementary_operator( cudensitymatElementaryOperator_t cudm_elem_op = nullptr; - std::vector interleaved_matrix; - interleaved_matrix.reserve(flat_matrix.size() * 2); - - for (const auto &value : flat_matrix) { - interleaved_matrix.push_back(value.real()); - interleaved_matrix.push_back(value.imag()); - } + // FIXME: leak (need to track this buffer somewhere and delete **after** the + // whole evolve) + auto *elementaryMat_d = create_array_gpu(flat_matrix); cudensitymatStatus_t status = cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, static_cast(interleaved_matrix.data()), - {nullptr, nullptr}, &cudm_elem_op); + CUDA_C_64F, elementaryMat_d, {nullptr, nullptr}, &cudm_elem_op); if (status != CUDENSITYMAT_STATUS_SUCCESS) { std::cerr << "Error: Failed to create elementary operator. Status: " @@ -92,7 +87,7 @@ void append_elementary_operator_to_term( std::vector elem_ops = {elem_op}; std::vector modeActionDuality(degrees.size(), 0); - + assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle, term, static_cast(degrees.size()), elem_ops.data(), degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), @@ -233,13 +228,15 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); + // FIXME: leak + // We must track these handles and destroy **after** evolve finishes // Destroy the term - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - // Cleanup - for (auto &elem_op : elementary_operators) { - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - } + // // Cleanup + // for (auto &elem_op : elementary_operators) { + // HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + // } } return operator_handle; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index c44bde5d2a..2da81e8c29 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -120,6 +120,8 @@ std::vector> cudm_state::get_raw_data() const { return rawData_; } +void *cudm_state::get_device_pointer() const { return gpuData_; } + std::vector cudm_state::get_hilbert_space_dims() const { return hilbertSpaceDims_; } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 1bfe69c79d..c84555ebed 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -91,3 +91,28 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepZeroStepSize) { TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { EXPECT_NO_THROW(time_stepper_->compute(*state_, 1e6, 1e3)); } + +TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { + const std::vector> initialState = { + {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + const std::vector dims = {4}; + auto inputState = std::make_unique(handle_, initialState, dims); + auto op = cudaq::matrix_operator::create(0); + auto cudmOp = cudaq::convert_to_cudensitymat_operator( + handle_, {}, op, dims); // Initialize the time stepper + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(*inputState, 0.0, 1.0); + + std::vector> outputStateVec(4); + HANDLE_CUDA_ERROR(cudaMemcpy( + outputStateVec.data(), outputState.get_device_pointer(), + outputStateVec.size() * sizeof(std::complex), cudaMemcpyDefault)); + // Create operator move the state up 1 step. + const std::vector> expectedOutputState = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + + for (std::size_t i = 0; i < expectedOutputState.size(); ++i) { + EXPECT_TRUE(std::abs(expectedOutputState[i] - outputStateVec[i]) < 1e-12); + } + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); +} From bc43e40cc3128dde5b445ee5359c6aee53c3e721 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 7 Feb 2025 05:12:36 +0000 Subject: [PATCH 104/311] Fix RK 1st order and add a test Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_state.h | 5 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 10 ++- runtime/cudaq/dynamics/cudm_state.cpp | 39 ++++++++---- .../cudaq/dynamics/runge_kutta_integrator.cpp | 35 ++++++----- .../dynamics/test_runge_kutta_integrator.cpp | 61 +++++++++++++++++++ 5 files changed, 121 insertions(+), 29 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 227dcb63e4..4e1a0d2180 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -60,6 +60,9 @@ class cudm_state { /// @return String representation of the state data. std::string dump() const; + /// @brief Dump the state data to the console for debugging purposes. + void dumpDeviceData() const; + /// @brief Convert the state vector to a density matrix. /// @return A new cudm_state representing the density matrix. cudm_state to_density_matrix() const; @@ -94,7 +97,7 @@ class cudm_state { /// @brief Scalar multiplication operator /// @return The new state after multiplying scalar with the current state. - cudm_state operator*(double scalar) const; + cudm_state &operator*=(const std::complex &scalar); private: std::vector> rawData_; diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 5d8ab4d0c2..c48125b33e 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -218,14 +218,22 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( } else if (const auto *scalar_op = dynamic_cast( &component)) { + // FIXME: do we need this code path? + // The product_op already has get_coefficient method. auto coeff = scalar_op->evaluate(parameters); append_scalar_to_term(handle, term, coeff); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); } } + // Handle the coefficient + auto coeff = product_op.get_coefficient().evaluate(parameters); // Append the product operator term to the top-level operator HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + handle, operator_handle, term, 0, + make_cuDoubleComplex(coeff.real(), coeff.imag()), {nullptr, nullptr})); // FIXME: leak diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 2da81e8c29..7cd0bdf127 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -167,20 +167,18 @@ cudm_state &cudm_state::operator+=(const cudm_state &other) { return *this; } +cudm_state &cudm_state::operator*=(const std::complex &scalar) { + void *gpuScalar; + HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &scalar, sizeof(std::complex), + cudaMemcpyHostToDevice)); -cudm_state cudm_state::operator*(double scalar) const { - cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); - - double *gpuScalar; - cudaMalloc(reinterpret_cast(&gpuScalar), sizeof(double)); - cudaMemcpy(gpuScalar, &scalar, sizeof(double), cudaMemcpyHostToDevice); - - HANDLE_CUDM_ERROR(cudensitymatStateComputeScaling(handle_, result.get_impl(), - gpuScalar, 0)); + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(handle_, state_, gpuScalar, 0)); - cudaFree(gpuScalar); + HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); - return result; + return *this; } std::string cudm_state::dump() const { @@ -200,6 +198,25 @@ std::string cudm_state::dump() const { return oss.str(); } +void cudm_state::dumpDeviceData() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + std::vector> hostBuffer(rawData_.size()); + HANDLE_CUDA_ERROR(cudaMemcpy(hostBuffer.data(), get_device_pointer(), + hostBuffer.size() * sizeof(std::complex), + cudaMemcpyDefault)); + std::cout << "State data: ["; + for (size_t i = 0; i < hostBuffer.size(); i++) { + std::cout << hostBuffer[i]; + if (i < hostBuffer.size() - 1) { + std::cout << ", "; + } + } + std::cout << "]\n"; +} + cudm_state cudm_state::to_density_matrix() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 87b7f48819..56441cea09 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -30,31 +30,34 @@ void runge_kutta_integrator::integrate(double target_time) { while (this->t < target_time) { double step_size = std::min(dt, target_time - this->t); - std::cout << "Runge-Kutta step at time " << this->t - << " with step size: " << step_size << std::endl; + // std::cout << "Runge-Kutta step at time " << this->t + // << " with step size: " << step_size << std::endl; if (this->substeps_ == 1) { // Euler method (1st order) cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= step_size; this->state += k1; } else if (this->substeps_ == 2) { + // FIXME: implement it // Midpoint method (2nd order) - cudm_state k1 = - this->stepper->compute(this->state, this->t, step_size / 2.0); - cudm_state k2 = - this->stepper->compute(k1, this->t + step_size / 2.0, step_size); - this->state += (k1 + k2) * 0.5; + // cudm_state k1 = + // this->stepper->compute(this->state, this->t, step_size / 2.0); + // cudm_state k2 = + // this->stepper->compute(k1, this->t + step_size / 2.0, step_size); + // this->state += (k1 + k2) * 0.5; } else if (this->substeps_ == 4) { + // FIXME: implement it // Runge-Kutta method (4th order) - cudm_state k1 = - this->stepper->compute(this->state, this->t, step_size / 2.0); - cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, - step_size / 2.0); - cudm_state k3 = - this->stepper->compute(k2, this->t + step_size / 2.0, step_size); - cudm_state k4 = - this->stepper->compute(k3, this->t + step_size, step_size); - this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); + // cudm_state k1 = + // this->stepper->compute(this->state, this->t, step_size / 2.0); + // cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, + // step_size / 2.0); + // cudm_state k3 = + // this->stepper->compute(k2, this->t + step_size / 2.0, step_size); + // cudm_state k4 = + // this->stepper->compute(k3, this->t + step_size, step_size); + // this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } // Update time diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 9541316100..48a5852629 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -147,3 +147,64 @@ TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { 0.0, time_stepper_, 3), std::invalid_argument); } + +TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { + const std::vector> initialState = {{1.0, 0.0}, + {0.0, 0.0}}; + const std::vector dims = {2}; + const std::string op_id = "pauli_x"; + auto func = [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = matrix_2(2, 2); + mat[{1, 0}] = 1.0; + mat[{0, 1}] = 1.0; + return mat; + }; + cudaq::matrix_operator::define(op_id, {-1}, func); + + // FIXME: enable all orders + // for (int integratorOrder : {1, 2, 4}) { + for (int integratorOrder : {1}) { + std::cout << "Test RK order " << integratorOrder << "\n"; + auto op = cudaq::product_operator( + std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, + cudaq::matrix_operator(op_id, {0})); + auto cudmOp = + cudaq::convert_to_cudensitymat_operator( + handle_, {}, op, dims); + auto time_stepper = std::make_shared(handle_, cudmOp); + + auto eulerIntegrator = std::make_unique( + cudm_state(handle_, initialState, dims), 0.0, time_stepper, + integratorOrder); + eulerIntegrator->set_option("dt", 0.001); + + constexpr std::size_t numDataPoints = 10; + double t = 0.0; + std::vector> outputStateVec(2); + for (std::size_t i = 1; i < numDataPoints; ++i) { + eulerIntegrator->integrate(i); + auto [t, state] = eulerIntegrator->get_state(); + // std::cout << "Time = " << t << "\n"; + // state.dumpDeviceData(); + HANDLE_CUDA_ERROR( + cudaMemcpy(outputStateVec.data(), state.get_device_pointer(), + outputStateVec.size() * sizeof(std::complex), + cudaMemcpyDefault)); + // Check state vector norm + EXPECT_NEAR(std::norm(outputStateVec[0]) + std::norm(outputStateVec[1]), + 1.0, 1e-2); + const double expValZ = + std::norm(outputStateVec[0]) - std::norm(outputStateVec[1]); + // Analytical results + EXPECT_NEAR(outputStateVec[0].real(), std::cos(2.0 * M_PI * 0.1 * t), + 1e-2); + } + + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); + } +} From 3d8b5982098374e167e155df3e85f34587d8b306 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 21:44:47 -0800 Subject: [PATCH 105/311] Adding unittests for cudm_op_conversion Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 2 + runtime/cudaq/cudm_op_conversion.h | 8 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 28 ++++ runtime/cudaq/dynamics/cudm_op_conversion.cpp | 70 ++++++++-- runtime/cudaq/dynamics/cudm_state.cpp | 2 +- unittests/CMakeLists.txt | 1 + unittests/dynamics/test_cudm_helpers.cpp | 2 +- .../dynamics/test_cudm_op_conversion.cpp | 129 ++++++++++++++++++ unittests/dynamics/test_mocks.h | 7 + 9 files changed, 229 insertions(+), 20 deletions(-) create mode 100644 unittests/dynamics/test_cudm_op_conversion.cpp diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index ede4ee0243..ff56ea0541 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -17,6 +17,8 @@ #include namespace cudaq { +std::vector> flatten_matrix(const matrix_2 &matrix); + void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream); diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 50395f0c56..c37c7b7cb3 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -39,11 +39,13 @@ class cudm_op_conversion { // Addition of two operator terms std::variant + std::complex> add(const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2); // Evaluate an operator and convert it to cudensitymatOperatorTerm_t std::variant> flatten_matrix(const matrix_2 &matrix) { @@ -51,6 +53,9 @@ cudensitymatElementaryOperator_t create_elementary_operator( throw std::invalid_argument("subspace_extents cannot be empty."); } + std::cout << "Subspace extents size: " << subspace_extents.size() + << ", Matrix size: " << flat_matrix.size() << std::endl; + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; // FIXME: leak (need to track this buffer somewhere and delete **after** the @@ -84,8 +89,20 @@ void append_elementary_operator_to_term( throw std::invalid_argument("elem_op cannot be null."); } + std::cout << "Appending Elementary Operator to Term" + << " - Degrees: " << degrees.size() << " - Term: " << term + << std::endl; + + for (int degree : degrees) { + if (degree < 0) { + throw std::out_of_range("Degree cannot be negative!"); + } + } + std::vector elem_ops = {elem_op}; + std::cout << "elem_ops.data(): " << elem_ops.data() << std::endl; + std::vector modeActionDuality(degrees.size(), 0); assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( @@ -111,8 +128,19 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, throw std::invalid_argument("Invalid state provided to scale_state."); } + cudaEvent_t start, stop; + cudaEventCreate(&start); + cudaEventCreate(&stop); + cudaEventRecord(start, stream); + HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); + + cudaEventRecord(stop, stream); + cudaEventSynchronize(stop); + float milliseconds = 0; + cudaEventElapsedTime(&milliseconds, start, stop); + std::cout << "Time taken: " << milliseconds << " ms" << std::endl; } cudensitymatOperator_t diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index b16f4231f4..c60179975d 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -18,7 +18,16 @@ namespace cudaq { cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, const std::map &dimensions, std::shared_ptr schedule) - : handle_(handle), dimensions_(dimensions), schedule_(schedule) {} + : handle_(handle), dimensions_(dimensions), schedule_(schedule) { + std::cout << "Handle: " << handle_ << std::endl; + if (handle_ == nullptr) { + throw std::runtime_error("Handle cannot be null."); + } + + if (dimensions_.empty()) { + throw std::invalid_argument("Dimensions map must not be empty."); + } +} cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( const cudensitymatWrappedScalarCallback_t &scalar) { @@ -91,19 +100,37 @@ cudm_op_conversion::mul( } std::variant -cudm_op_conversion::add( - const std::variant &op1, - const std::variant &op2) { - if (std::holds_alternative(op1) || - std::holds_alternative(op2)) { - return std::get(op1) + std::get(op2); + std::complex> +cudm_op_conversion::add(const std::variant> &op1, + const std::variant> &op2) { + if (std::holds_alternative>(op1) && + std::holds_alternative>(op2)) { + return std::get>(op1) + + std::get>(op2); } + if (std::holds_alternative>(op1)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op1))), + std::get(op2)); + } + + if (std::holds_alternative>(op2)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op2))), + std::get(op1)); + } + + // FIXME: Need to check later + int32_t num_space_modes = + std::max(static_cast(dimensions_.size()), 1); + cudensitymatOperatorTerm_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, num_space_modes, nullptr, &result)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( @@ -136,18 +163,31 @@ cudm_op_conversion::evaluate( if (std::holds_alternative(op)) { const matrix_operator &mat_op = std::get(op); + std::vector space_mode_extents; + for (const auto &dim : dimensions_) { + space_mode_extents.push_back(dim.second); + } + cudensitymatOperatorTerm_t opterm; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle_, dimensions_.size(), nullptr, &opterm)); + handle_, dimensions_.size(), space_mode_extents.data(), &opterm)); cudensitymatElementaryOperator_t elem_op; cudensitymatWrappedTensorCallback_t callback = _wrap_callback_tensor(mat_op); + auto flat_matrix = flatten_matrix(mat_op.to_matrix(dimensions_, {})); + + void *tensor_data = create_array_gpu(flat_matrix); + if (!tensor_data) { + throw std::runtime_error( + "Failed to allocate GPU memory for tensor_data."); + } + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle_, mat_op.degrees.size(), nullptr, - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, nullptr, - callback, &elem_op)); + handle_, mat_op.degrees.size(), space_mode_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, + tensor_data, callback, &elem_op)); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle_, opterm, 1, &elem_op, mat_op.degrees.data(), nullptr, diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 7cd0bdf127..72901abba1 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -28,7 +28,7 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, // Allocate device memory size_t dataSize = rawData_.size() * sizeof(std::complex); - cudaMalloc(reinterpret_cast(&gpuData_), dataSize); + HANDLE_CUDA_ERROR(cudaMalloc(reinterpret_cast(&gpuData_), dataSize)); // Copy data from host to device HANDLE_CUDA_ERROR( diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index a5db4a6718..99160e52f1 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -295,6 +295,7 @@ if (CUDA_FOUND) dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp + dynamics/test_cudm_op_conversion.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index c5d5e9759c..aed09951a7 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -50,7 +50,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { ASSERT_TRUE(state.is_initialized()); - EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), 2.0, stream)); + EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), {2.0}, stream)); } // Test for compute_lindblad_op diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp new file mode 100644 index 0000000000..122f68993d --- /dev/null +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include "cudaq/cudm_op_conversion.h" +#include "cudaq/operators.h" +#include "test_mocks.h" +#include +#include +#include + +using namespace cudaq; + +class CuDmOpConversion : public ::testing::Test { +protected: + cudensitymatHandle_t handle; + std::map dimensions; + std::shared_ptr schedule; + std::unique_ptr converter; + std::vector space_mode_extents; + + void SetUp() override { + handle = mock_handle(); + dimensions = {{0, 2}, {1, 2}}; + for (const auto &dim : dimensions) { + space_mode_extents.push_back(dim.second); + } + schedule = std::shared_ptr(); + converter = std::make_unique(handle, dimensions, schedule); + } +}; + +TEST_F(CuDmOpConversion, ConstructorValid) { + EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); +} + +TEST_F(CuDmOpConversion, ConstructorEmptyDimensions) { + std::map empty_dimensions; + EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), std::invalid_argument); +} + +TEST_F(CuDmOpConversion, ConstructorInvalidHandle) { + cudensitymatHandle_t invalid_handle = nullptr; + EXPECT_THROW(cudm_op_conversion converter(invalid_handle, dimensions, schedule), std::runtime_error); +} + +TEST_F(CuDmOpConversion, EvaluateScalarConstant) { + scalar_operator scalar_op(2.5); + auto result = converter->evaluate(scalar_op); + + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), std::complex(2.5, 0.0)); +} + +TEST_F(CuDmOpConversion, EvaluateScalarCallback) { + scalar_operator scalar_op([](std::map>) { + return std::complex(1.0, -1.0); + }); + auto result = converter->evaluate(scalar_op); + + ASSERT_TRUE(std::holds_alternative(result)); +} + +// TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { +// matrix_operator mat_op("H", {0}); +// auto result = converter->evaluate(mat_op); + +// ASSERT_TRUE(std::holds_alternative(result)); +// } + +TEST_F(CuDmOpConversion, EvaluateProductOperator) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(0); + product_operator product_op = op0 * op1; + EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); +} + +TEST_F(CuDmOpConversion, AddOperators) { + scalar_operator scalar_op1(2.0); + scalar_operator scalar_op2(3.0); + + auto result = converter->add(converter->evaluate(scalar_op1), converter->evaluate(scalar_op2)); + + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); +} + +TEST_F(CuDmOpConversion, AddComplexScalars) { + std::complex scalar_1(2.0, 1.0); + std::complex scalar_2(3.0, -1.0); + + auto result = converter->add(scalar_1, scalar_2); + + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); +} + +// TEST_F(CuDmOpConversion, AddScalarAndOperator) { +// scalar_operator scalar_op(1.0); +// matrix_operator mat_op("X", {0}); + +// auto scalar_result = converter->evaluate(scalar_op); +// auto op_result = converter->evaluate(mat_op); + +// auto final_result = converter->add(scalar_result, op_result); + +// ASSERT_TRUE(std::holds_alternative(final_result)); +// } + +TEST_F(CuDmOpConversion, TensorProductOfScalars) { + auto result = converter->tensor(2.0, 3.0); + EXPECT_TRUE(std::holds_alternative(result)); + EXPECT_EQ(std::get(result), 6.0); +} + +// TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { +// cudensitymatOperatorTerm_t op_term; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, dimensions.size(), space_mode_extents.data(), &op_term)); + +// auto result = converter->tensor(2.0, op_term); +// EXPECT_TRUE(std::holds_alternative(result)); + +// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); +// } diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 7715b14a7d..801e0d2541 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -14,6 +14,13 @@ #include #include +// Mock cudensitymatHandle_t +inline cudensitymatHandle_t mock_handle() { + cudensitymatHandle_t handle; + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + return handle; +} + // Mock Liouvillian operator creation inline cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { cudensitymatOperator_t liouvillian = nullptr; From 90972a63f71b4e598a809a7bff71ea365cad80b8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 13:48:31 -0800 Subject: [PATCH 106/311] Adding operator* method Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 4 ++++ runtime/cudaq/dynamics/cudm_state.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 4e1a0d2180..a1b84148ca 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -99,6 +99,10 @@ class cudm_state { /// @return The new state after multiplying scalar with the current state. cudm_state &operator*=(const std::complex &scalar); + cudm_state operator*(double scalar) &&; + + double norm() const; + private: std::vector> rawData_; std::complex *gpuData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 72901abba1..f863616b37 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -181,6 +181,21 @@ cudm_state &cudm_state::operator*=(const std::complex &scalar) { return *this; } +cudm_state cudm_state::operator*(double scalar) && { + this->operator*=(scalar); + return std::move(*this); +} + +double cudm_state::norm() const { + std::vector> rawData = this->get_raw_data(); + + double norm = 0.0; + for (const auto &val : rawData) { + norm += std::norm(val); + } + return std::sqrt(norm); +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From 04b85b00ee04a292d3adf8715539b0e1f22fa0c6 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 13:57:05 -0800 Subject: [PATCH 107/311] Removing norm method from state Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 2 -- runtime/cudaq/dynamics/cudm_state.cpp | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index a1b84148ca..1c7082cdfe 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -101,8 +101,6 @@ class cudm_state { cudm_state operator*(double scalar) &&; - double norm() const; - private: std::vector> rawData_; std::complex *gpuData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index f863616b37..3b837101d3 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -186,16 +186,6 @@ cudm_state cudm_state::operator*(double scalar) && { return std::move(*this); } -double cudm_state::norm() const { - std::vector> rawData = this->get_raw_data(); - - double norm = 0.0; - for (const auto &val : rawData) { - norm += std::norm(val); - } - return std::sqrt(norm); -} - std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From 18f6072b3d614ca0ab3d6942e648639f1fca6b4b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 15:02:50 -0800 Subject: [PATCH 108/311] Fixing midpoint integration Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 21 +++++++++++++------ .../dynamics/test_runge_kutta_integrator.cpp | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 56441cea09..62c5a46390 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -39,13 +39,17 @@ void runge_kutta_integrator::integrate(double target_time) { k1 *= step_size; this->state += k1; } else if (this->substeps_ == 2) { - // FIXME: implement it // Midpoint method (2nd order) - // cudm_state k1 = - // this->stepper->compute(this->state, this->t, step_size / 2.0); - // cudm_state k2 = - // this->stepper->compute(k1, this->t + step_size / 2.0, step_size); - // this->state += (k1 + k2) * 0.5; + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= (step_size / 2.0); + + this->state += k1; + + cudm_state k2 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k2 *= (step_size / 2.0); + + this->state += k2; } else if (this->substeps_ == 4) { // FIXME: implement it // Runge-Kutta method (4th order) @@ -60,6 +64,11 @@ void runge_kutta_integrator::integrate(double target_time) { // this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } + // double norm_factor = this->state.norm(); + // if (norm_factor > 1e-8) { + // this->state *= (1.0 / norm_factor); + // } + // Update time this->t += step_size; } diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 48a5852629..fce0fef120 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -168,7 +168,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { // FIXME: enable all orders // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1}) { + for (int integratorOrder : {1, 2}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, @@ -183,7 +183,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { integratorOrder); eulerIntegrator->set_option("dt", 0.001); - constexpr std::size_t numDataPoints = 10; + constexpr std::size_t numDataPoints = 2; double t = 0.0; std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { From 3c22ad5eabfc8743ebb23d2803d6832a7c2b94dd Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 15:56:31 -0800 Subject: [PATCH 109/311] Setting the accumulation for 4 states Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 36 +++++++++++-------- .../dynamics/test_runge_kutta_integrator.cpp | 4 +-- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 62c5a46390..66147ac559 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -53,21 +53,29 @@ void runge_kutta_integrator::integrate(double target_time) { } else if (this->substeps_ == 4) { // FIXME: implement it // Runge-Kutta method (4th order) - // cudm_state k1 = - // this->stepper->compute(this->state, this->t, step_size / 2.0); - // cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, - // step_size / 2.0); - // cudm_state k3 = - // this->stepper->compute(k2, this->t + step_size / 2.0, step_size); - // cudm_state k4 = - // this->stepper->compute(k3, this->t + step_size, step_size); - // this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); - } + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= (step_size / 2.0); + + this->state += k1; + + cudm_state k2 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k2 *= (step_size / 2.0); + + this->state += k2; + + cudm_state k3 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k3 *= (step_size / 2.0); + + this->state += k3; - // double norm_factor = this->state.norm(); - // if (norm_factor > 1e-8) { - // this->state *= (1.0 / norm_factor); - // } + cudm_state k4 = + this->stepper->compute(this->state, this->t + step_size, step_size); + k4 *= (step_size / 2.0); + + this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); + } // Update time this->t += step_size; diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index fce0fef120..803c969a65 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -168,7 +168,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { // FIXME: enable all orders // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1, 2}) { + for (int integratorOrder : {1, 2, 4}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, @@ -183,7 +183,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { integratorOrder); eulerIntegrator->set_option("dt", 0.001); - constexpr std::size_t numDataPoints = 2; + constexpr std::size_t numDataPoints = 10; double t = 0.0; std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { From d279c0703d55c77756aa0201c4c1c36001489cd2 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 20:07:41 -0800 Subject: [PATCH 110/311] Disabling RK4 for now Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 31 ++++++++++--------- .../dynamics/test_runge_kutta_integrator.cpp | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 66147ac559..04ae89e595 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -53,28 +53,29 @@ void runge_kutta_integrator::integrate(double target_time) { } else if (this->substeps_ == 4) { // FIXME: implement it // Runge-Kutta method (4th order) - cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); - k1 *= (step_size / 2.0); + // cudm_state k1 = this->stepper->compute(this->state, this->t, + // step_size); k1 *= (step_size / 2.0); - this->state += k1; + // this->state += k1; - cudm_state k2 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k2 *= (step_size / 2.0); + // cudm_state k2 = this->stepper->compute( + // this->state, this->t + step_size / 2.0, step_size); + // k2 *= (step_size / 2.0); - this->state += k2; + // this->state += k2; - cudm_state k3 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k3 *= (step_size / 2.0); + // cudm_state k3 = this->stepper->compute( + // this->state, this->t + step_size / 2.0, step_size); + // k3 *= step_size; - this->state += k3; + // this->state += k3; - cudm_state k4 = - this->stepper->compute(this->state, this->t + step_size, step_size); - k4 *= (step_size / 2.0); + // cudm_state k4 = + // this->stepper->compute(this->state, this->t + step_size, + // step_size); + // k4 *= step_size; - this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); + // this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); } // Update time diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 803c969a65..4de2dd526d 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -168,7 +168,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { // FIXME: enable all orders // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1, 2, 4}) { + for (int integratorOrder : {1, 2}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, From d5eb3fbec7fc8fab855bd1dd4224df7df6dbde3f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 21:29:36 -0800 Subject: [PATCH 111/311] * Adding mode_action_duality * Adding mock function to create a matrix operator * Adding unittests for mul * Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 11 +- .../dynamics/test_cudm_op_conversion.cpp | 139 +++++++++++------- unittests/dynamics/test_mocks.h | 25 ++++ 3 files changed, 117 insertions(+), 58 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index c60179975d..3d069b7d08 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -184,14 +184,17 @@ cudm_op_conversion::evaluate( "Failed to allocate GPU memory for tensor_data."); } + std::vector mode_action_duality( + mat_op.degrees.size(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE); + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( handle_, mat_op.degrees.size(), space_mode_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, - tensor_data, callback, &elem_op)); + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), + CUDA_C_64F, tensor_data, callback, &elem_op)); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle_, opterm, 1, &elem_op, mat_op.degrees.data(), nullptr, - {1.0, 0.0}, {nullptr, nullptr})); + handle_, opterm, 1, &elem_op, mat_op.degrees.data(), + mode_action_duality.data(), {1.0, 0.0}, {nullptr, nullptr})); return opterm; } diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index 122f68993d..0b112a7f03 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -6,103 +6,112 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include #include "cudaq/cudm_op_conversion.h" #include "cudaq/operators.h" #include "test_mocks.h" -#include #include +#include #include +#include using namespace cudaq; class CuDmOpConversion : public ::testing::Test { protected: - cudensitymatHandle_t handle; - std::map dimensions; - std::shared_ptr schedule; - std::unique_ptr converter; - std::vector space_mode_extents; - - void SetUp() override { - handle = mock_handle(); - dimensions = {{0, 2}, {1, 2}}; - for (const auto &dim : dimensions) { - space_mode_extents.push_back(dim.second); - } - schedule = std::shared_ptr(); - converter = std::make_unique(handle, dimensions, schedule); + cudensitymatHandle_t handle; + std::map dimensions; + std::shared_ptr schedule; + std::unique_ptr converter; + std::vector space_mode_extents; + + void SetUp() override { + handle = mock_handle(); + dimensions = {{0, 2}, {1, 2}}; + for (const auto &dim : dimensions) { + space_mode_extents.push_back(dim.second); } + schedule = std::shared_ptr(); + converter = + std::make_unique(handle, dimensions, schedule); + } }; TEST_F(CuDmOpConversion, ConstructorValid) { - EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); + EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); } TEST_F(CuDmOpConversion, ConstructorEmptyDimensions) { - std::map empty_dimensions; - EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), std::invalid_argument); + std::map empty_dimensions; + EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), + std::invalid_argument); } TEST_F(CuDmOpConversion, ConstructorInvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; - EXPECT_THROW(cudm_op_conversion converter(invalid_handle, dimensions, schedule), std::runtime_error); + cudensitymatHandle_t invalid_handle = nullptr; + EXPECT_THROW( + cudm_op_conversion converter(invalid_handle, dimensions, schedule), + std::runtime_error); } TEST_F(CuDmOpConversion, EvaluateScalarConstant) { - scalar_operator scalar_op(2.5); - auto result = converter->evaluate(scalar_op); + scalar_operator scalar_op(2.5); + auto result = converter->evaluate(scalar_op); - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), std::complex(2.5, 0.0)); + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), + std::complex(2.5, 0.0)); } TEST_F(CuDmOpConversion, EvaluateScalarCallback) { - scalar_operator scalar_op([](std::map>) { - return std::complex(1.0, -1.0); - }); - auto result = converter->evaluate(scalar_op); + scalar_operator scalar_op([](std::map>) { + return std::complex(1.0, -1.0); + }); + auto result = converter->evaluate(scalar_op); - ASSERT_TRUE(std::holds_alternative(result)); + ASSERT_TRUE( + std::holds_alternative(result)); } -// TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { -// matrix_operator mat_op("H", {0}); -// auto result = converter->evaluate(mat_op); +TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { + matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); + auto result = converter->evaluate(mat_op); -// ASSERT_TRUE(std::holds_alternative(result)); -// } + ASSERT_TRUE(std::holds_alternative(result)); +} TEST_F(CuDmOpConversion, EvaluateProductOperator) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(0); - product_operator product_op = op0 * op1; - EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(0); + product_operator product_op = op0 * op1; + EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); } TEST_F(CuDmOpConversion, AddOperators) { - scalar_operator scalar_op1(2.0); - scalar_operator scalar_op2(3.0); + scalar_operator scalar_op1(2.0); + scalar_operator scalar_op2(3.0); - auto result = converter->add(converter->evaluate(scalar_op1), converter->evaluate(scalar_op2)); + auto result = converter->add(converter->evaluate(scalar_op1), + converter->evaluate(scalar_op2)); - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), + std::complex(5.0, 0.0)); } TEST_F(CuDmOpConversion, AddComplexScalars) { - std::complex scalar_1(2.0, 1.0); - std::complex scalar_2(3.0, -1.0); + std::complex scalar_1(2.0, 1.0); + std::complex scalar_2(3.0, -1.0); - auto result = converter->add(scalar_1, scalar_2); + auto result = converter->add(scalar_1, scalar_2); - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), + std::complex(5.0, 0.0)); } // TEST_F(CuDmOpConversion, AddScalarAndOperator) { // scalar_operator scalar_op(1.0); -// matrix_operator mat_op("X", {0}); +// matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); // auto scalar_result = converter->evaluate(scalar_op); // auto op_result = converter->evaluate(mat_op); @@ -113,17 +122,39 @@ TEST_F(CuDmOpConversion, AddComplexScalars) { // } TEST_F(CuDmOpConversion, TensorProductOfScalars) { - auto result = converter->tensor(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative(result)); - EXPECT_EQ(std::get(result), 6.0); + auto result = converter->tensor(2.0, 3.0); + EXPECT_TRUE(std::holds_alternative(result)); + EXPECT_EQ(std::get(result), 6.0); } // TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { // cudensitymatOperatorTerm_t op_term; -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, dimensions.size(), space_mode_extents.data(), &op_term)); +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, +// dimensions.size(), space_mode_extents.data(), &op_term)); // auto result = converter->tensor(2.0, op_term); // EXPECT_TRUE(std::holds_alternative(result)); // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); // } + +TEST_F(CuDmOpConversion, MultiplyOperators) { + auto result = converter->mul(2.0, 3.0); + EXPECT_TRUE(std::holds_alternative(result)); + EXPECT_EQ(std::get(result), 6.0); +} + +TEST_F(CuDmOpConversion, MoveSemantics) { + scalar_operator scalar_op(1.5); + auto scalar_result = converter->evaluate(scalar_op); + + EXPECT_TRUE(std::holds_alternative>(scalar_result)); + + auto moved_result = std::move(scalar_result); + + EXPECT_TRUE(std::holds_alternative>(moved_result)); + EXPECT_EQ(std::get>(moved_result), + std::complex(1.5, 0.0)); + + EXPECT_TRUE(scalar_result.index() != std::variant_npos); +} diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 801e0d2541..dcb7a20bf0 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -8,12 +8,37 @@ #pragma once +#include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" #include #include #include #include #include +// Mock matrix_operator +inline cudaq::matrix_operator mock_matrix_operator(const std::string &op_id, + int qubit_index) { + try { + auto callback = [](std::vector dimensions, + std::map>) { + if (dimensions.size() != 1 || dimensions[0] != 2) { + throw std::invalid_argument("Invalid dimensions for operator."); + } + + cudaq::matrix_2 matrix(2, 2); + matrix[{0, 1}] = 1.0; + matrix[{1, 0}] = 1.0; + return matrix; + }; + + cudaq::matrix_operator::define(op_id, {-1}, callback); + } catch (...) { + } + + return cudaq::matrix_operator(op_id, {qubit_index}); +} + // Mock cudensitymatHandle_t inline cudensitymatHandle_t mock_handle() { cudensitymatHandle_t handle; From 29ab7441790d49889ef47286e470f1b53ad1858d Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 8 Feb 2025 17:14:00 -0800 Subject: [PATCH 112/311] Fixing AddComplexScalars unittest Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 2 + runtime/cudaq/dynamics/cudm_op_conversion.cpp | 63 ++++++++++++++++--- .../dynamics/test_cudm_op_conversion.cpp | 16 ++--- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index c37c7b7cb3..d69d7266c8 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -66,5 +66,7 @@ class cudm_op_conversion { cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &op); cudensitymatWrappedTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); + + std::vector> get_identity_matrix(); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 3d069b7d08..4a31d41b29 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -19,7 +19,6 @@ cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, const std::map &dimensions, std::shared_ptr schedule) : handle_(handle), dimensions_(dimensions), schedule_(schedule) { - std::cout << "Handle: " << handle_ << std::endl; if (handle_ == nullptr) { throw std::runtime_error("Handle cannot be null."); } @@ -29,19 +28,48 @@ cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, } } +std::vector> cudm_op_conversion::get_identity_matrix() { + std::vector> identity_matrix( + dimensions_.size() * dimensions_.size(), {0.0, 0.0}); + for (size_t i = 0; i < dimensions_.size(); i++) { + identity_matrix[i * dimensions_.size() + i] = {1.0, 0.0}; + } + + return identity_matrix; +} + cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( const cudensitymatWrappedScalarCallback_t &scalar) { cudensitymatOperatorTerm_t op_term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), - nullptr, &op_term)); + + std::vector space_mode_extents; + for (const auto &dim : dimensions_) { + space_mode_extents.push_back(dim.second); + } + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); + + void *tensor_data = create_array_gpu(get_identity_matrix()); + if (!tensor_data) { + throw std::runtime_error("Failed to allocate GPU memory for tensor_data."); + } + + std::vector mode_action_duality(dimensions_.size(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE); cudensitymatElementaryOperator_t identity; HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle_, 1, nullptr, CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, nullptr, {nullptr, nullptr}, &identity)); + handle_, dimensions_.size(), space_mode_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), + CUDA_C_64F, tensor_data, {nullptr, nullptr}, &identity)); + + std::vector states_modes_acted_on(dimensions_.size()); + std::iota(states_modes_acted_on.begin(), states_modes_acted_on.end(), 0); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle_, op_term, 1, &identity, nullptr, nullptr, {1.0, 0.0}, scalar)); + handle_, op_term, 1, &identity, states_modes_acted_on.data(), + mode_action_duality.data(), {1.0, 0.0}, scalar)); return op_term; } @@ -53,12 +81,27 @@ cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( throw std::invalid_argument("Invalid operator term (nullptr)."); } + std::vector space_mode_extents; + for (const auto &dim : dimensions_) { + space_mode_extents.push_back(dim.second); + } + + cudensitymatOperatorTerm_t scalar_term = _scalar_to_op(scalar); + cudensitymatOperatorTerm_t new_opterm; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), - nullptr, &new_opterm)); + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle_, static_cast(dimensions_.size()), + space_mode_extents.data(), &new_opterm)); + + std::vector mode_action_duality(dimensions_.size(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_opterm, op, 0, - {1.0, 0.0}, scalar)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, new_opterm, scalar_term, mode_action_duality.size(), {1.0, 0.0}, + scalar)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, new_opterm, op, mode_action_duality.size(), {1.0, 0.0}, scalar)); return new_opterm; } diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index 0b112a7f03..a59c8d41ac 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -109,17 +109,17 @@ TEST_F(CuDmOpConversion, AddComplexScalars) { std::complex(5.0, 0.0)); } -// TEST_F(CuDmOpConversion, AddScalarAndOperator) { -// scalar_operator scalar_op(1.0); -// matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); +TEST_F(CuDmOpConversion, AddScalarAndOperator) { + scalar_operator scalar_op(1.0); + matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); -// auto scalar_result = converter->evaluate(scalar_op); -// auto op_result = converter->evaluate(mat_op); + auto scalar_result = converter->evaluate(scalar_op); + auto op_result = converter->evaluate(mat_op); -// auto final_result = converter->add(scalar_result, op_result); + auto final_result = converter->add(scalar_result, op_result); -// ASSERT_TRUE(std::holds_alternative(final_result)); -// } + ASSERT_TRUE(std::holds_alternative(final_result)); +} TEST_F(CuDmOpConversion, TensorProductOfScalars) { auto result = converter->tensor(2.0, 3.0); From 0c83b3399f04f89544beabe2b8658b840528eca8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 8 Feb 2025 18:10:37 -0800 Subject: [PATCH 113/311] Fixing AddScalarAndOperator unittest Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 4a31d41b29..5728bdd7c3 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -29,10 +29,14 @@ cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, } std::vector> cudm_op_conversion::get_identity_matrix() { - std::vector> identity_matrix( - dimensions_.size() * dimensions_.size(), {0.0, 0.0}); - for (size_t i = 0; i < dimensions_.size(); i++) { - identity_matrix[i * dimensions_.size() + i] = {1.0, 0.0}; + size_t dim = 1; + for (const auto &entry : dimensions_) { + dim *= entry.second; + } + + std::vector> identity_matrix(dim * dim, {0.0, 0.0}); + for (size_t i = 0; i < dim; i++) { + identity_matrix[i * dim + i] = {1.0, 0.0}; } return identity_matrix; @@ -40,13 +44,12 @@ std::vector> cudm_op_conversion::get_identity_matrix() { cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( const cudensitymatWrappedScalarCallback_t &scalar) { - cudensitymatOperatorTerm_t op_term; - std::vector space_mode_extents; for (const auto &dim : dimensions_) { space_mode_extents.push_back(dim.second); } + cudensitymatOperatorTerm_t op_term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); @@ -74,7 +77,7 @@ cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( return op_term; } -cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( +cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( const cudensitymatWrappedScalarCallback_t &scalar, const cudensitymatOperatorTerm_t &op) { if (!op) { @@ -86,24 +89,29 @@ cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( space_mode_extents.push_back(dim.second); } - cudensitymatOperatorTerm_t scalar_term = _scalar_to_op(scalar); + cudensitymatOperatorTerm_t scalar_op = _scalar_to_op(scalar); - cudensitymatOperatorTerm_t new_opterm; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + if (!scalar_op) { + throw std::runtime_error("scalar_op is NULL."); + } + + cudensitymatOperator_t new_op; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle_, static_cast(dimensions_.size()), - space_mode_extents.data(), &new_opterm)); + space_mode_extents.data(), &new_op)); std::vector mode_action_duality(dimensions_.size(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, new_opterm, scalar_term, mode_action_duality.size(), {1.0, 0.0}, - scalar)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_op, scalar_op, + mode_action_duality.size(), + {1.0, 0.0}, scalar)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, new_opterm, op, mode_action_duality.size(), {1.0, 0.0}, scalar)); + handle_, new_op, op, mode_action_duality.size(), {1.0, 0.0}, + {nullptr, nullptr})); - return new_opterm; + return new_op; } std::variant Date: Sat, 8 Feb 2025 21:37:57 -0800 Subject: [PATCH 114/311] * Changing return type from double to std::complex * Adding function to get space_mode_extents * Adding more unittests for op_conversion Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 18 ++-- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 87 +++++++++++++------ .../dynamics/test_cudm_op_conversion.cpp | 66 +++++++++++--- 3 files changed, 124 insertions(+), 47 deletions(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index d69d7266c8..645542c78b 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -23,19 +23,23 @@ class cudm_op_conversion { // Tensor product of two operator terms std::variant + std::complex> tensor(const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2); // Multiplication of two operator terms std::variant + std::complex> mul(const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2); // Addition of two operator terms std::variant> get_identity_matrix(); + + std::vector get_space_mode_extents(); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 5728bdd7c3..022b4af1a3 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -42,13 +42,19 @@ std::vector> cudm_op_conversion::get_identity_matrix() { return identity_matrix; } -cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( - const cudensitymatWrappedScalarCallback_t &scalar) { +std::vector cudm_op_conversion::get_space_mode_extents() { std::vector space_mode_extents; for (const auto &dim : dimensions_) { space_mode_extents.push_back(dim.second); } + return space_mode_extents; +} + +cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( + const cudensitymatWrappedScalarCallback_t &scalar) { + std::vector space_mode_extents = get_space_mode_extents(); + cudensitymatOperatorTerm_t op_term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); @@ -84,10 +90,7 @@ cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( throw std::invalid_argument("Invalid operator term (nullptr)."); } - std::vector space_mode_extents; - for (const auto &dim : dimensions_) { - space_mode_extents.push_back(dim.second); - } + std::vector space_mode_extents = get_space_mode_extents(); cudensitymatOperatorTerm_t scalar_op = _scalar_to_op(scalar); @@ -115,20 +118,49 @@ cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( } std::variant + std::complex> cudm_op_conversion::tensor( const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2) { - if (std::holds_alternative(op1) || - std::holds_alternative(op2)) { - return std::get(op1) * std::get(op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2) { + if (std::holds_alternative>(op1) && + std::holds_alternative>(op2)) { + return std::get>(op1) * + std::get>(op2); } - cudensitymatOperatorTerm_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), - nullptr, &result)); + if (std::holds_alternative>(op1)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op1))), + std::get(op2)); + } + + if (std::holds_alternative>(op2)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op2))), + std::get(op1)); + } + + if (std::holds_alternative(op1)) { + return tensor( + _scalar_to_op(std::get(op1)), + std::get(op2)); + } + + if (std::holds_alternative(op2)) { + return tensor( + _scalar_to_op(std::get(op2)), + std::get(op1)); + } + + std::vector space_mode_extents = get_space_mode_extents(); + + cudensitymatOperator_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle_, dimensions_.size(), space_mode_extents.data(), &result)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle_, result, std::get(op1), 0, {1.0, 0.0}, @@ -141,12 +173,13 @@ cudm_op_conversion::tensor( } std::variant -cudm_op_conversion::mul( - const std::variant &op1, - const std::variant &op2) { + std::complex> +cudm_op_conversion::mul(const std::variant> &op1, + const std::variant> &op2) { return tensor(op1, op2); } @@ -179,10 +212,11 @@ cudm_op_conversion::add(const std::variant(dimensions_.size()), 1); + std::vector space_mode_extents = get_space_mode_extents(); - cudensitymatOperatorTerm_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, num_space_modes, - nullptr, &result)); + cudensitymatOperator_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle_, num_space_modes, space_mode_extents.data(), &result)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle_, result, std::get(op1), 0, {1.0, 0.0}, @@ -214,10 +248,7 @@ cudm_op_conversion::evaluate( if (std::holds_alternative(op)) { const matrix_operator &mat_op = std::get(op); - std::vector space_mode_extents; - for (const auto &dim : dimensions_) { - space_mode_extents.push_back(dim.second); - } + std::vector space_mode_extents = get_space_mode_extents(); cudensitymatOperatorTerm_t opterm; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index a59c8d41ac..bf8a0c7403 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -121,27 +121,67 @@ TEST_F(CuDmOpConversion, AddScalarAndOperator) { ASSERT_TRUE(std::holds_alternative(final_result)); } +TEST_F(CuDmOpConversion, AddMatrixOperators) { + matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); + matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + + auto op_result1 = converter->evaluate(mat_op1); + auto op_result2 = converter->evaluate(mat_op2); + + auto final_result = converter->add(op_result1, op_result2); + + ASSERT_TRUE(std::holds_alternative(final_result)); +} + +TEST_F(CuDmOpConversion, MultiplyMatrixOperators) { + matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); + matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + + auto op_result1 = converter->evaluate(mat_op1); + auto op_result2 = converter->evaluate(mat_op2); + + auto final_result = converter->mul(op_result1, op_result2); + + ASSERT_TRUE(std::holds_alternative(final_result)); +} + +TEST_F(CuDmOpConversion, TensorOfMatrixOperators) { + matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); + matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + + auto op_result1 = converter->evaluate(mat_op1); + auto op_result2 = converter->evaluate(mat_op2); + + auto final_result = converter->tensor(op_result1, op_result2); + + ASSERT_TRUE(std::holds_alternative(final_result)); +} + TEST_F(CuDmOpConversion, TensorProductOfScalars) { auto result = converter->tensor(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative(result)); - EXPECT_EQ(std::get(result), 6.0); + EXPECT_TRUE(std::holds_alternative>(result)); + std::complex final_result = std::get>(result); + EXPECT_EQ(final_result.real(), 6); + EXPECT_EQ(final_result.imag(), 0); } -// TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { -// cudensitymatOperatorTerm_t op_term; -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, -// dimensions.size(), space_mode_extents.data(), &op_term)); +TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { + cudensitymatOperatorTerm_t op_term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, dimensions.size(), space_mode_extents.data(), &op_term)); -// auto result = converter->tensor(2.0, op_term); -// EXPECT_TRUE(std::holds_alternative(result)); + auto result = converter->tensor(2.0, op_term); + EXPECT_TRUE(std::holds_alternative(result)); -// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); -// } + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); +} TEST_F(CuDmOpConversion, MultiplyOperators) { - auto result = converter->mul(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative(result)); - EXPECT_EQ(std::get(result), 6.0); + auto result = converter->mul(6.0, 3.0); + EXPECT_TRUE(std::holds_alternative>(result)); + std::complex final_result = std::get>(result); + EXPECT_EQ(final_result.real(), 18); + EXPECT_EQ(final_result.imag(), 0); } TEST_F(CuDmOpConversion, MoveSemantics) { From 1e949610a8a8d337549bcffbc4d183c69a068ba9 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sun, 9 Feb 2025 15:51:59 -0800 Subject: [PATCH 115/311] Fixing RK4 Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 2 +- runtime/cudaq/dynamics/cudm_state.cpp | 9 ++++-- .../cudaq/dynamics/runge_kutta_integrator.cpp | 32 +++++++++---------- .../dynamics/test_runge_kutta_integrator.cpp | 4 +-- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 1c7082cdfe..8dff591bc7 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -99,7 +99,7 @@ class cudm_state { /// @return The new state after multiplying scalar with the current state. cudm_state &operator*=(const std::complex &scalar); - cudm_state operator*(double scalar) &&; + cudm_state operator*(double scalar) const; private: std::vector> rawData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 3b837101d3..7b20c677a9 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -181,9 +181,12 @@ cudm_state &cudm_state::operator*=(const std::complex &scalar) { return *this; } -cudm_state cudm_state::operator*(double scalar) && { - this->operator*=(scalar); - return std::move(*this); +cudm_state cudm_state::operator*(double scalar) const { + cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + + result *= std::complex(scalar, 0.0); + + return result; } std::string cudm_state::dump() const { diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 04ae89e595..4217edb48b 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -51,31 +51,29 @@ void runge_kutta_integrator::integrate(double target_time) { this->state += k2; } else if (this->substeps_ == 4) { - // FIXME: implement it // Runge-Kutta method (4th order) - // cudm_state k1 = this->stepper->compute(this->state, this->t, - // step_size); k1 *= (step_size / 2.0); + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= step_size; - // this->state += k1; + this->state += k1 * 0.5; - // cudm_state k2 = this->stepper->compute( - // this->state, this->t + step_size / 2.0, step_size); - // k2 *= (step_size / 2.0); + cudm_state k2 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k2 *= step_size; - // this->state += k2; + this->state += k2 * 0.5; - // cudm_state k3 = this->stepper->compute( - // this->state, this->t + step_size / 2.0, step_size); - // k3 *= step_size; + cudm_state k3 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k3 *= step_size; - // this->state += k3; + this->state += k3; - // cudm_state k4 = - // this->stepper->compute(this->state, this->t + step_size, - // step_size); - // k4 *= step_size; + cudm_state k4 = + this->stepper->compute(this->state, this->t + step_size, step_size); + k4 *= step_size; - // this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); + this->state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); } // Update time diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 4de2dd526d..95eacce432 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -166,9 +166,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { }; cudaq::matrix_operator::define(op_id, {-1}, func); - // FIXME: enable all orders - // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1, 2}) { + for (int integratorOrder : {1, 2, 4}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, From 23bfb2b7b795e11292ab6e414f35fad728b133ce Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 10 Feb 2025 00:08:38 +0000 Subject: [PATCH 116/311] Add expectation Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_expectation.h | 28 +++++++ runtime/cudaq/dynamics/CMakeLists.txt | 1 + runtime/cudaq/dynamics/cudm_expectation.cpp | 77 ++++++++++++++++++++ unittests/CMakeLists.txt | 1 + unittests/dynamics/test_cudm_expectation.cpp | 53 ++++++++++++++ 5 files changed, 160 insertions(+) create mode 100644 runtime/cudaq/cudm_expectation.h create mode 100644 runtime/cudaq/dynamics/cudm_expectation.cpp create mode 100644 unittests/dynamics/test_cudm_expectation.cpp diff --git a/runtime/cudaq/cudm_expectation.h b/runtime/cudaq/cudm_expectation.h new file mode 100644 index 0000000000..f345d70df1 --- /dev/null +++ b/runtime/cudaq/cudm_expectation.h @@ -0,0 +1,28 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include +namespace cudaq { + +class cudm_expectation { + cudensitymatHandle_t m_handle; + cudensitymatOperator_t m_hamOp; + cudensitymatExpectation_t m_expectation; + cudensitymatWorkspaceDescriptor_t m_workspace; + +public: + cudm_expectation(cudensitymatHandle_t handle, cudensitymatOperator_t op); + ~cudm_expectation(); + void prepare(cudensitymatState_t state); + std::complex compute(cudensitymatState_t state, double time); + +}; + +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 95d56934f1..7f2510fb96 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -27,6 +27,7 @@ set(CUDAQ_OPS_SRC helpers.cpp rydberg_hamiltonian.cpp cudm_op_conversion.cpp + cudm_expectation.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_expectation.cpp b/runtime/cudaq/dynamics/cudm_expectation.cpp new file mode 100644 index 0000000000..18f4b54b70 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_expectation.cpp @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_expectation.h" +#include "common/Logger.h" +#include "cudaq/cudm_error_handling.h" +#include "cudaq/cudm_helpers.h" +#include + +namespace cudaq { +cudm_expectation::cudm_expectation(cudensitymatHandle_t handle, + cudensitymatOperator_t op) + : m_handle(handle), m_hamOp(op) { + HANDLE_CUDM_ERROR( + cudensitymatCreateExpectation(m_handle, m_hamOp, &m_expectation)); + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(m_handle, &m_workspace)); +} + +cudm_expectation::~cudm_expectation() { + cudensitymatDestroyWorkspace(m_workspace); + cudensitymatDestroyExpectation(m_expectation); +} + +void cudm_expectation::prepare(cudensitymatState_t state) { + std::size_t freeMem = 0, totalMem = 0; + HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); + freeMem = static_cast(static_cast(freeMem) * 0.80); + + HANDLE_CUDM_ERROR(cudensitymatExpectationPrepare( + m_handle, m_expectation, state, CUDENSITYMAT_COMPUTE_64F, freeMem, + m_workspace, 0x0)); +} +std::complex cudm_expectation::compute(cudensitymatState_t state, + double time) { + // TODO: create a global scratch buffer + std::size_t requiredBufferSize = 0; + HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( + m_handle, m_workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); + + void *workspaceBuffer = nullptr; + if (requiredBufferSize > 0) { + cudaq::info("Required buffer size for expectation compute: {}", + requiredBufferSize); + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + workspaceBuffer = create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + m_handle, m_workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + } + + auto *expectationValue_d = + create_array_gpu(std::vector>(1, {0.0, 0.0})); + HANDLE_CUDM_ERROR(cudensitymatExpectationCompute( + m_handle, m_expectation, time, 0, nullptr, state, expectationValue_d, + m_workspace, 0x0)); + std::complex result; + HANDLE_CUDA_ERROR(cudaMemcpy(&result, expectationValue_d, + sizeof(std::complex), + cudaMemcpyDefault)); + destroy_array_gpu(expectationValue_d); + if (workspaceBuffer) { + destroy_array_gpu(workspaceBuffer); + } + return result; +} +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 99160e52f1..adbd82a980 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -296,6 +296,7 @@ if (CUDA_FOUND) dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp dynamics/test_cudm_op_conversion.cpp + dynamics/test_cudm_expectation.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp new file mode 100644 index 0000000000..b86e1cb37f --- /dev/null +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "test_mocks.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace cudaq; + +class CuDensityExpectationTest : public ::testing::Test { +protected: + cudensitymatHandle_t handle_; + + void SetUp() override { + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + } + + void TearDown() override { + // Clean up + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + } +}; + +TEST_F(CuDensityExpectationTest, checkCompute) { + const std::vector dims = {10}; + // Check number operator on boson Fock space + auto op = cudaq::matrix_operator::number(0); + auto cudmOp = cudaq::convert_to_cudensitymat_operator( + handle_, {}, op, dims); + + cudm_expectation expectation(handle_, cudmOp); + + for (std::size_t stateIdx = 0; stateIdx < dims[0]; ++stateIdx) { + std::vector> initialState(dims[0], 0.0); + initialState[stateIdx] = 1.0; + auto inputState = std::make_unique(handle_, initialState, dims); + expectation.prepare(inputState->get_impl()); + const auto expVal = expectation.compute(inputState->get_impl(), 0.0); + EXPECT_NEAR(expVal.real(), 1.0 * stateIdx, 1e-12); + EXPECT_NEAR(expVal.imag(), 0.0, 1e-12); + } +} From 526e4b289ff02ab2bbcb4e31e1b3b7d1e7e7ab8e Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sun, 9 Feb 2025 16:18:53 -0800 Subject: [PATCH 117/311] Using cudensitymatStateComputeScaling for multiplication of state with a scalar Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_state.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 7b20c677a9..281ee5acce 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -182,9 +182,20 @@ cudm_state &cudm_state::operator*=(const std::complex &scalar) { } cudm_state cudm_state::operator*(double scalar) const { - cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + void *gpuScalar; + HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); + + std::complex complexScalar(scalar, 0.0); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &complexScalar, + sizeof(std::complex), + cudaMemcpyHostToDevice)); - result *= std::complex(scalar, 0.0); + cudm_state result(handle_, rawData_, hilbertSpaceDims_); + + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(handle_, result.state_, gpuScalar, 0)); + + HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); return result; } From 962f130e6f88dc4a657f7e29efb8b2d9d336d050 Mon Sep 17 00:00:00 2001 From: "A.M. Santana" <39563805+anthony-santana@users.noreply.github.com> Date: Thu, 9 Jan 2025 12:37:13 -0500 Subject: [PATCH 118/311] Merge in dynamics work and squash commit history (#12) * add skeleton of new API functions without connecting to docs yet Signed-off-by: A.M. Santana * updates Signed-off-by: A.M. Santana * boilerplate Signed-off-by: A.M. Santana * clean up linking bug Signed-off-by: A.M. Santana * adding schedule implementation * fixing typo * small updates Signed-off-by: A.M. Santana * Cleaning up docs preview for PR #6. * push initial tests that show memory leak in current translation to complex matrix Signed-off-by: A.M. Santana * completely work around eigen in default elementary ops Signed-off-by: A.M. Santana * storing changes Signed-off-by: A.M. Santana * start to build out callback function class Signed-off-by: A.M. Santana * working function wrapper implementation Signed-off-by: A.M. Santana * implement complex matrix equality operator Signed-off-by: A.M. Santana * push with broken to matrix overload Signed-off-by: A.M. Santana * fix scoping issue found in to matrix Signed-off-by: A.M. Santana * fill out unit tests for to matrix overload Signed-off-by: A.M. Santana * add skeleton of new API functions without connecting to docs yet Signed-off-by: A.M. Santana * updates Signed-off-by: A.M. Santana * boilerplate Signed-off-by: A.M. Santana * clean up linking bug Signed-off-by: A.M. Santana * initial scalar value support and tests Signed-off-by: A.M. Santana * update to_value to evaluate to amtch python api Signed-off-by: A.M. Santana * adding kwargs capability in C++ by using std::variant and std::bind * renaming VariantArg to NumericType to match Python * adding a std::variant returntype nad adjusting the test accordingly * Cleaning up docs preview for PR #7. * commit first draft of arithmetic against complex doubles Signed-off-by: A.M. Santana * little build errors Signed-off-by: A.M. Santana * call generator directly instead of evaluate in operator overloads Signed-off-by: A.M. Santana * remove old constructor Signed-off-by: A.M. Santana * copy constructor Signed-off-by: A.M. Santana * push partially working arithmetic operations Signed-off-by: A.M. Santana * comment back in tests Signed-off-by: A.M. Santana * remove constructor that takes removed parameters member Signed-off-by: A.M. Santana * still having memory issues Signed-off-by: A.M. Santana * implement and test remaining pre defined elementary ops except displace and squeeze. implement complex matrix exponential Signed-off-by: A.M. Santana * fix for segfault in copy constructor but still have memory issues from arithmetic Signed-off-by: A.M. Santana * temp patch to get scalar arithmetic against complex doubles working. against doubles wrapped in functions still broken Signed-off-by: A.M. Santana * potential fix for callback function going out of scope Signed-off-by: A.M. Santana * confirm fix for scalar ops from functions and reenable tests Signed-off-by: A.M. Santana * begin to support scalar against scalar ops and simplify other arithmetic definitions with macros Signed-off-by: A.M. Santana * support for remaining arithmetic against other scalar ops except for assignment operators Signed-off-by: A.M. Santana * add checks to ensure local variables are picked up fine in generator functions Signed-off-by: A.M. Santana * clean up test file Signed-off-by: A.M. Santana * full refactor to take a [arameter map with elementary operator implementation back under construction Signed-off-by: A.M. Santana * fix elementary ops Signed-off-by: A.M. Santana * start to manually merge in operator sum changes Signed-off-by: A.M. Santana * camel case some things and underscore some others Signed-off-by: A.M. Santana * fix build errors from header file Signed-off-by: A.M. Santana * refactor implementation file names, delete unused dynamics.h, refactor unittests with new dynamics folder Signed-off-by: A.M. Santana * storing large set of changes to dynamics folder structure and implementing more arithmetic Signed-off-by: A.M. Santana * add cudaq tensor type Signed-off-by: Alex McCaskey * add uint8 tensor to python api Signed-off-by: Alex McCaskey * Update unittests/utils/tensor_tester.cpp Signed-off-by: Eric Schweitz * Update unittests/utils/tensor_tester.cpp Signed-off-by: Eric Schweitz * Update unittests/utils/tensor_tester.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update python/runtime/utils/py_tensor.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/extension_point.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/extension_point.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/tensor.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/tensor.h Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/impls/xtensor_impl.cpp Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/tensor_impl.h Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/tensor_impl.h Signed-off-by: Eric Schweitz * Update runtime/cudaq/utils/details/tensor_impl.h Signed-off-by: Eric Schweitz * Spelling fixes. Signed-off-by: Eric Schweitz * push current state Signed-off-by: A.M. Santana * Format Signed-off-by: Anna Gringauze * Format Signed-off-by: Anna Gringauze * Drop debug code. Signed-off-by: Eric Schweitz * Cleanup tensor types a tad. Signed-off-by: Eric Schweitz * Fix spelling Signed-off-by: Anna Gringauze * Fix spelling and format python code Signed-off-by: Anna Gringauze * Sort spelling allowlist Signed-off-by: Anna Gringauze * Cleaning up docs preview for PR #9. * more arithmetic support Signed-off-by: A.M. Santana * Update spelling and formatting Signed-off-by: Anna Gringauze * more implementation and more tests Signed-off-by: A.M. Santana * Update spelling and formatting Signed-off-by: Anna Gringauze * This is an attempt to sort out the different ownership models of tensor data. The python stub for "take" needs to be implemented however. The semantics are: - copy : the tensor object makes a copy of the data and owns the copy. i.e. there is a unique_ptr. - take : the tensor object steals a copy of the data from the caller. In order for this case to make sense, we want the caller to guarantee that the data is unique before we steal it. This can be done by forcing the client code to wrap the tensor data in a unique_ptr *before* the take call. - borrow : In this case, the tensor object has no ownership of the tensor data and just naively assumes the client will manage the data correctly. In this case, a raw pointer to the client's data is used. Signed-off-by: Eric Schweitz * Update the missing py_tensor code. Signed-off-by: Eric Schweitz * Add handling for empty shape case. Signed-off-by: Eric Schweitz * Add more python tests Signed-off-by: Anna Gringauze * Add a take() with move semantics. Signed-off-by: Eric Schweitz * fix product operator constructor issue and tests Signed-off-by: A.M. Santana * fix more test more Signed-off-by: A.M. Santana * cover all arithmetic Signed-off-by: A.M. Santana * update before merging in tensor pr Signed-off-by: A.M. Santana * Fix __init__ argument order so tests pass. The shape must come first. Signed-off-by: Eric Schweitz * Remove StateTensor. (Not used.) Signed-off-by: Eric Schweitz * Add more comments on how to use this stuff. Signed-off-by: Eric Schweitz * Added python tests and fixed some issues * Merge with tensor Signed-off-by: Anna Gringauze * DCO Remediation Commit for Anna Gringauze I, Anna Gringauze , hereby add my Signed-off-by to this commit: e78def4e1370e18f497ca891884d69e9f4421f3b Signed-off-by: Anna Gringauze * Add some boilerplate for tensor operators. Signed-off-by: Eric Schweitz * Make the compiler work a bit harder. Signed-off-by: Eric Schweitz * Add move constructor. Signed-off-by: Eric Schweitz * Make sure the return values for operators work as expected. Signed-off-by: Eric Schweitz * clang-format Signed-off-by: Eric Schweitz * Add override to dtor. Signed-off-by: Eric Schweitz * Add forward decls. Signed-off-by: Eric Schweitz * More fussy templates. Signed-off-by: Eric Schweitz * Workaround warnings from g++. Signed-off-by: Eric Schweitz * Fix typos. Signed-off-by: Eric Schweitz * Support copy semantics for Numpy 2.0 * DCO Remediation Commit for Anna Gringauze I, Anna Gringauze , hereby add my Signed-off-by to this commit: 85fae3771663c4c2e8659fa5c2110ef87d362efd Signed-off-by: Anna Gringauze * Try fixing doc gen and c++ compilation errors Signed-off-by: Anna Gringauze * Add compilation test for nvcc Signed-off-by: Anna Gringauze * Remove temp file Signed-off-by: Anna Gringauze * store changes Signed-off-by: A.M. Santana * first pass of updating return types to cudaq tensor Signed-off-by: A.M. Santana * store working version with tests before rebase Signed-off-by: A.M. Santana * fix improper merge resolution issues Signed-off-by: A.M. Santana * more issues with resolving merge Signed-off-by: A.M. Santana * first pass of translation to matrix_2 with build errors fixed. now double checking tests Signed-off-by: A.M. Santana * fix remaining artifacts from rebase Signed-off-by: A.M. Santana * fix matrix checks for simple elementary op unit tests Signed-off-by: A.M. Santana * first pass of implementing deeper matrix checks in tests. Have two files left to finish Signed-off-by: A.M. Santana * fix copyright headers Signed-off-by: A.M. Santana * minor change to check verified commit Signed-off-by: A.M. Santana --------- Signed-off-by: A.M. Santana Signed-off-by: Alex McCaskey Signed-off-by: Eric Schweitz Signed-off-by: Anna Gringauze Co-authored-by: Sachin Pisal Co-authored-by: cuda-quantum-bot Co-authored-by: Alex McCaskey Co-authored-by: Eric Schweitz Co-authored-by: Ben Howe <141149032+bmhowe23@users.noreply.github.com> Co-authored-by: Anna Gringauze --- docs/sphinx/api/languages/cpp_api.rst | 47 ++ runtime/cudaq/CMakeLists.txt | 5 +- runtime/cudaq/definition.h | 136 ++++ runtime/cudaq/dynamics/CMakeLists.txt | 37 ++ runtime/cudaq/dynamics/definition.cpp | 37 ++ .../cudaq/dynamics/elementary_operators.cpp | 469 ++++++++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 368 +++++++++++ runtime/cudaq/dynamics/product_operators.cpp | 271 ++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 275 ++++++++ runtime/cudaq/dynamics/schedule.cpp | 81 +++ runtime/cudaq/operator_utils.h | 40 ++ runtime/cudaq/operators.h | 464 ++++++++++++++ runtime/cudaq/schedule.h | 105 +++ runtime/cudaq/utils/tensor.h | 3 + unittests/CMakeLists.txt | 26 + .../dynamics/elementary_ops_arithmetic.cpp | 604 ++++++++++++++++++ unittests/dynamics/elementary_ops_simple.cpp | 208 ++++++ unittests/dynamics/operator_sum.cpp | 572 +++++++++++++++++ .../dynamics/product_operators_arithmetic.cpp | 591 +++++++++++++++++ unittests/dynamics/scalar_ops_arithmetic.cpp | 505 +++++++++++++++ unittests/dynamics/scalar_ops_simple.cpp | 119 ++++ 21 files changed, 4961 insertions(+), 2 deletions(-) create mode 100644 runtime/cudaq/definition.h create mode 100644 runtime/cudaq/dynamics/CMakeLists.txt create mode 100644 runtime/cudaq/dynamics/definition.cpp create mode 100644 runtime/cudaq/dynamics/elementary_operators.cpp create mode 100644 runtime/cudaq/dynamics/operator_sum.cpp create mode 100644 runtime/cudaq/dynamics/product_operators.cpp create mode 100644 runtime/cudaq/dynamics/scalar_operators.cpp create mode 100644 runtime/cudaq/dynamics/schedule.cpp create mode 100644 runtime/cudaq/operator_utils.h create mode 100644 runtime/cudaq/operators.h create mode 100644 runtime/cudaq/schedule.h create mode 100644 unittests/dynamics/elementary_ops_arithmetic.cpp create mode 100644 unittests/dynamics/elementary_ops_simple.cpp create mode 100644 unittests/dynamics/operator_sum.cpp create mode 100644 unittests/dynamics/product_operators_arithmetic.cpp create mode 100644 unittests/dynamics/scalar_ops_arithmetic.cpp create mode 100644 unittests/dynamics/scalar_ops_simple.cpp diff --git a/docs/sphinx/api/languages/cpp_api.rst b/docs/sphinx/api/languages/cpp_api.rst index 34487dbefb..b4c12fac2b 100644 --- a/docs/sphinx/api/languages/cpp_api.rst +++ b/docs/sphinx/api/languages/cpp_api.rst @@ -223,6 +223,53 @@ Utilities .. doxygentypedef:: cudaq::real .. doxygenfunction:: cudaq::range(std::size_t) + +Dynamics +========= + +.. .. doxygenclass:: cudaq::EvolveResult + :members: + +.. .. doxygenclass:: cudaq::AsyncEvolveResult + :members: + +.. doxygenclass:: cudaq::operator_sum + :members: + +.. doxygenclass:: cudaq::product_operator + :members: + +.. doxygenclass:: cudaq::scalar_operator + :members: + +.. doxygenclass:: cudaq::elementary_operator + :members: + +.. doxygenclass:: cudaq::OperatorArithmetics + :members: + +.. doxygenclass:: cudaq::MatrixArithmetics + :members: + +.. doxygenclass:: cudaq::Schedule + :members: + +.. doxygenclass:: cudaq::operators + :members: + +.. doxygenclass:: cudaq::pauli + :members: + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, state initial_state, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, std::vector initial_states, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) + +.. .. doxygenfunction:: cudaq::evolve_async + Namespaces =========== diff --git a/runtime/cudaq/CMakeLists.txt b/runtime/cudaq/CMakeLists.txt index a4eac4f89d..d2f23bd0f9 100644 --- a/runtime/cudaq/CMakeLists.txt +++ b/runtime/cudaq/CMakeLists.txt @@ -40,7 +40,7 @@ if (CUDA_FOUND) PRIVATE .) target_link_libraries(${LIBRARY_NAME} - PUBLIC dl cudaq-spin cudaq-common cudaq-nlopt cudaq-ensmallen + PUBLIC dl cudaq-spin cudaq-operators cudaq-common cudaq-nlopt cudaq-ensmallen PRIVATE nvqir fmt::fmt-header-only CUDA::cudart_static) target_compile_definitions(${LIBRARY_NAME} PRIVATE CUDAQ_HAS_CUDA) @@ -52,7 +52,7 @@ else() PRIVATE .) target_link_libraries(${LIBRARY_NAME} - PUBLIC dl cudaq-spin cudaq-common cudaq-nlopt cudaq-ensmallen + PUBLIC dl cudaq-spin cudaq-operators cudaq-common cudaq-nlopt cudaq-ensmallen PRIVATE nvqir fmt::fmt-header-only) endif() @@ -61,6 +61,7 @@ add_subdirectory(algorithms) add_subdirectory(platform) add_subdirectory(builder) add_subdirectory(domains) +add_subdirectory(dynamics) install(TARGETS ${LIBRARY_NAME} EXPORT cudaq-targets DESTINATION lib) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h new file mode 100644 index 0000000000..bdf5af8ab5 --- /dev/null +++ b/runtime/cudaq/definition.h @@ -0,0 +1,136 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/qis/state.h" +#include "cudaq/utils/tensor.h" + +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +// Limit the signature of the users callback function to accept a vector of ints +// for the degree of freedom dimensions, and a vector of complex doubles for the +// concrete parameter values. +using Func = std::function, std::map>)>; + +class CallbackFunction { +private: + // The user provided callback function that takes the degrees of + // freedom and a vector of complex parameters. + Func _callback_func; + +public: + CallbackFunction() = default; + + template + CallbackFunction(Callable &&callable) { + static_assert( + std::is_invocable_r_v, + std::map>>, + "Invalid callback function. Must have signature " + "matrix_2(" + "std::map, " + "std::map>)"); + _callback_func = std::forward(callable); + } + + // Copy constructor. + CallbackFunction(CallbackFunction &other) { + _callback_func = other._callback_func; + } + + CallbackFunction(const CallbackFunction &other) { + _callback_func = other._callback_func; + } + + matrix_2 + operator()(std::map degrees, + std::map> parameters) const { + return _callback_func(std::move(degrees), std::move(parameters)); + } +}; + +using ScalarFunc = std::function( + std::map>)>; + +// A scalar callback function does not need to accept the dimensions, +// therefore we will use a different function type for this specific class. +class ScalarCallbackFunction : CallbackFunction { +private: + // The user provided callback function that takes a vector of parameters. + ScalarFunc _callback_func; + +public: + ScalarCallbackFunction() = default; + + template + ScalarCallbackFunction(Callable &&callable) { + static_assert( + std::is_invocable_r_v, Callable, + std::map>>, + "Invalid callback function. Must have signature std::complex(" + "std::map>)"); + _callback_func = std::forward(callable); + } + + // Copy constructor. + ScalarCallbackFunction(ScalarCallbackFunction &other) { + _callback_func = other._callback_func; + } + + ScalarCallbackFunction(const ScalarCallbackFunction &other) { + _callback_func = other._callback_func; + } + + bool operator!() { return (!_callback_func); } + + std::complex + operator()(std::map> parameters) const { + return _callback_func(std::move(parameters)); + } +}; + +/// @brief Object used to give an error if a Definition of an elementary +/// or scalar operator is instantiated by other means than the `define` +/// class method. +class Definition { +public: + std::string id; + + // The user-provided generator function should take a variable number of + // complex doubles for the parameters. It should return a + // `cudaq::tensor` type representing the operator + // matrix. + CallbackFunction generator; + + // Constructor. + Definition(); + + // Destructor. + ~Definition(); + + void create_definition(const std::string &operator_id, + std::map expected_dimensions, + CallbackFunction &&create); + + // To call the generator function + matrix_2 generate_matrix( + const std::map °rees, + const std::map> ¶meters) const; + +private: + // Member variables + std::map m_expected_dimensions; +}; +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt new file mode 100644 index 0000000000..9709cd9a71 --- /dev/null +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -0,0 +1,37 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +set(LIBRARY_NAME cudaq-operators) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") +set(INTERFACE_POSITION_INDEPENDENT_CODE ON) + +set(CUDAQ_OPS_SRC + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp +) + +add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) +set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) +target_include_directories(${LIBRARY_NAME} + PUBLIC + $ + $ + $ + PRIVATE .) + +set (OPERATOR_DEPENDENCIES "") +list(APPEND OPERATOR_DEPENDENCIES fmt::fmt-header-only) +add_openmp_configurations(${LIBRARY_NAME} OPERATOR_DEPENDENCIES) + +target_link_libraries(${LIBRARY_NAME} PRIVATE ${OPERATOR_DEPENDENCIES}) + +install(TARGETS ${LIBRARY_NAME} EXPORT cudaq-operator-targets DESTINATION lib) + +install(EXPORT cudaq-operator-targets + FILE CUDAQSpinTargets.cmake + NAMESPACE cudaq:: + DESTINATION lib/cmake/cudaq) diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp new file mode 100644 index 0000000000..cc357fbeab --- /dev/null +++ b/runtime/cudaq/dynamics/definition.cpp @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/definition.h" +#include "cudaq/qis/state.h" + +#include +#include +#include +#include + +namespace cudaq { + +Definition::Definition() = default; + +// Convenience setter +void Definition::create_definition(const std::string &operator_id, + std::map expected_dimensions, + CallbackFunction &&create) { + id = operator_id; + generator = std::move(create); + m_expected_dimensions = std::move(expected_dimensions); +} + +matrix_2 Definition::generate_matrix( + const std::map °rees, + const std::map> ¶meters) const { + return generator(degrees, parameters); +} + +Definition::~Definition() = default; +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp new file mode 100644 index 0000000000..137dde02cc --- /dev/null +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -0,0 +1,469 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include + +namespace cudaq { + +/// Elementary Operator constructor. +elementary_operator::elementary_operator(std::string operator_id, + std::vector degrees) + : id(operator_id), degrees(degrees) {} +elementary_operator::elementary_operator(const elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} +elementary_operator::elementary_operator(elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} + +elementary_operator elementary_operator::identity(int degree) { + std::string op_id = "identity"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + int degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + + // Build up the identity matrix. + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = 1.0 + 0.0 * 'j'; + } + + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::zero(int degree) { + std::string op_id = "zero"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + // Need to set the degree via the op itself because the + // argument to the outer function goes out of scope when + // the user invokes this later on via, e.g, `to_matrix()`. + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::annihilate(int degree) { + std::string op_id = "annihilate"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::create(int degree) { + std::string op_id = "create"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::position(int degree) { + std::string op_id = "position"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + // position = 0.5 * (create + annihilate) + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = + 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::momentum(int degree) { + std::string op_id = "momentum"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + // momentum = 0.5j * (create - annihilate) + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::number(int degree) { + std::string op_id = "number"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = static_cast(i) + 0.0j; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator elementary_operator::parity(int degree) { + std::string op_id = "parity"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&](std::map dimensions, + std::map> _none) { + auto degree = op.degrees[0]; + std::size_t dimension = dimensions[degree]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + } + std::cout << "dumping the complex mat: \n"; + std::cout << mat.dump(); + std::cout << "\ndone\n\n"; + return mat; + }; + op.define(op_id, op.expected_dimensions, func); + } + return op; +} + +elementary_operator +elementary_operator::displace(int degree, std::complex amplitude) { + std::string op_id = "displace"; + std::vector degrees = {degree}; + auto op = elementary_operator(op_id, degrees); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + // if (op.m_ops.find(op_id) == op.m_ops.end()) { + // auto func = [&](std::map dimensions, + // std::map> _none) { + // auto degree = op.degrees[0]; + // std::size_t dimension = dimensions[degree]; + // auto temp_mat = matrix_2(dimension, dimension); + // // // displace = exp[ (amplitude * create) - (conj(amplitude) * + // annihilate) ] + // // for (std::size_t i = 0; i + 1 < dimension; i++) { + // // temp_mat[{i + 1, i}] = + // // amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + // // temp_mat[{i, i + 1}] = + // // -1. * std::conj(amplitude) * std::sqrt(static_cast(i + + // 1)) + + // // 0.0 * 'j'; + // // } + // // Not ideal that our method of computing the matrix exponential + // // requires copies here. Maybe we can just use eigen directly here + // // to limit to one copy, but we can address that later. + // auto mat = temp_mat.exp(); + // std::cout << "dumping the complex mat: \n"; + // mat.dump(); + // std::cout << "\ndone\n\n"; + // return mat; + // }; + // op.define(op_id, op.expected_dimensions, func); + // } + throw std::runtime_error("currently have a bug in implementation."); + return op; +} + +elementary_operator +elementary_operator::squeeze(int degree, std::complex amplitude) { + throw std::runtime_error("Not yet implemented."); +} + +matrix_2 elementary_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + return m_ops[id].generator(dimensions, parameters); +} + +/// Elementary Operator Arithmetic. + +operator_sum elementary_operator::operator+(scalar_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + std::vector> _this = { + *this}; + std::vector> _other = { + other}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +operator_sum elementary_operator::operator-(scalar_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + std::vector> _this = { + *this}; + std::vector> _other = { + -1. * other}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +product_operator elementary_operator::operator*(scalar_operator other) { + std::vector> _args = { + *this, other}; + return product_operator(_args); +} + +operator_sum elementary_operator::operator+(std::complex other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator(other); + std::vector> _this = { + *this}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +operator_sum elementary_operator::operator-(std::complex other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator((-1. * other)); + std::vector> _this = { + *this}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +product_operator elementary_operator::operator*(std::complex other) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + *this, other_scalar}; + return product_operator(_args); +} + +operator_sum elementary_operator::operator+(double other) { + std::complex value(other, 0.0); + return *this + value; +} + +operator_sum elementary_operator::operator-(double other) { + std::complex value(other, 0.0); + return *this - value; +} + +product_operator elementary_operator::operator*(double other) { + std::complex value(other, 0.0); + return *this * value; +} + +operator_sum operator+(std::complex other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(std::complex other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(std::complex other, + elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +operator_sum operator+(double other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(double other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(double other, elementary_operator self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +product_operator elementary_operator::operator*(elementary_operator other) { + std::vector> _args = { + *this, other}; + return product_operator(_args); +} + +operator_sum elementary_operator::operator+(elementary_operator other) { + std::vector> _this = { + *this}; + std::vector> _other = { + other}; + return operator_sum({product_operator(_this), product_operator(_other)}); +} + +operator_sum elementary_operator::operator-(elementary_operator other) { + std::vector> _this = { + *this}; + return operator_sum({product_operator(_this), (-1. * other)}); +} + +operator_sum elementary_operator::operator+(operator_sum other) { + std::vector> _this = { + *this}; + std::vector _prods = {product_operator(_this)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + other; +} + +operator_sum elementary_operator::operator-(operator_sum other) { + std::vector> _this = { + *this}; + std::vector _prods = {product_operator(_this)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - other; +} + +operator_sum elementary_operator::operator*(operator_sum other) { + std::vector> _this = { + *this}; + std::vector _prods = {product_operator(_this)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * other; +} + +operator_sum elementary_operator::operator+(product_operator other) { + std::vector> _this = { + *this}; + return operator_sum({product_operator(_this), other}); +} + +operator_sum elementary_operator::operator-(product_operator other) { + return *this + (-1. * other); +} + +product_operator elementary_operator::operator*(product_operator other) { + std::vector> other_terms = + other.get_terms(); + /// Insert this elementary operator to the front of the terms list. + other_terms.insert(other_terms.begin(), *this); + return product_operator(other_terms); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp new file mode 100644 index 0000000000..a0ba70cb2b --- /dev/null +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -0,0 +1,368 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include + +namespace cudaq { + +/// Operator sum constructor given a vector of product operators. +operator_sum::operator_sum(const std::vector &terms) + : m_terms(terms) {} + +// std::vector> +// operator_sum::canonicalize_product(product_operator &prod) const { +// std::vector> +// canonicalized_terms; + +// std::vector all_degrees; +// std::vector scalars; +// std::vector non_scalars; + +// for (const auto &op : prod.get_terms()) { +// if (std::holds_alternative(op)) { +// scalars.push_back(*std::get(op)); +// } else { +// non_scalars.push_back(*std::get(op)); +// all_degrees.insert(all_degrees.end(), +// std::get(op).degrees.begin(), +// std::get(op).degrees.end()); +// } +// } + +// if (all_degrees.size() == +// std::set(all_degrees.begin(), all_degrees.end()).size()) { +// std::sort(non_scalars.begin(), non_scalars.end(), +// [](const elementary_operator &a, const elementary_operator &b) { +// return a.degrees < b.degrees; +// }); +// } + +// for (size_t i = 0; std::min(scalars.size(), non_scalars.size()); i++) { +// canonicalized_terms.push_back(std::make_tuple(scalars[i], non_scalars[i])); +// } + +// return canonicalized_terms; +// } + +// std::vector> +// operator_sum::_canonical_terms() const { +// std::vector> terms; +// // for (const auto &term : m_terms) { +// // auto canonicalized = canonicalize_product(term); +// // terms.insert(terms.end(), canonicalized.begin(), canonicalized.end()); +// // } + +// // std::sort(terms.begin(), terms.end(), [](const auto &a, const auto &b) { +// // // return std::to_string(product_operator(a)) < +// // // std::to_string(product_operator(b)); +// // return product_operator(a).to_string() < +// product_operator(b).to_string(); +// // }); + +// return terms; +// } + +// operator_sum operator_sum::canonicalize() const { +// std::vector canonical_terms; +// for (const auto &term : _canonical_terms()) { +// canonical_terms.push_back(product_operator(term)); +// } +// return operator_sum(canonical_terms); +// } + +// bool operator_sum::operator==(const operator_sum &other) const { +// return _canonical_terms() == other._canonical_terms(); +// } + +// // Degrees property +// std::vector operator_sum::degrees() const { +// std::set unique_degrees; +// for (const auto &term : m_terms) { +// for (const auto &op : term.get_terms()) { +// unique_degrees.insert(op.get_degrees().begin(), +// op.get_degrees().end()); +// } +// } + +// return std::vector(unique_degrees.begin(), unique_degrees.end()); +// } + +// // Parameters property +// std::map operator_sum::parameters() const { +// std::map param_map; +// for (const auto &term : m_terms) { +// for (const auto &op : term.get_terms()) { +// auto op_params = op.parameters(); +// param_map.insert(op_params.begin(), op.params.end()); +// } +// } + +// return param_map; +// } + +// // Check if all terms are spin operators +// bool operator_sum::_is_spinop() const { +// return std::all_of( +// m_terms.begin(), m_terms.end(), [](product_operator &term) { +// return std::all_of(term.get_terms().begin(), +// term.get_terms().end(), +// [](const Operator &op) { return op.is_spinop(); +// }); +// }); +// } + +// Arithmetic operators +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector combined_terms = m_terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.m_terms.begin()), + std::make_move_iterator(other.m_terms.end())); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator-(const operator_sum &other) const { + return *this + (-1 * other); +} + +operator_sum operator_sum::operator-=(const operator_sum &other) { + *this = *this - other; + return *this; +} + +operator_sum operator_sum::operator+=(const operator_sum &other) { + *this = *this + other; + return *this; +} + +operator_sum operator_sum::operator*(operator_sum &other) const { + auto self_terms = m_terms; + std::vector product_terms; + auto other_terms = other.get_terms(); + for (auto &term : self_terms) { + for (auto &other_term : other_terms) { + product_terms.push_back(term * other_term); + } + } + return operator_sum(product_terms); +} + +operator_sum operator_sum::operator*=(operator_sum &other) { + *this = *this * other; + return *this; +} + +operator_sum operator_sum::operator*(const scalar_operator &other) const { + std::vector combined_terms = m_terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator+(const scalar_operator &other) const { + std::vector combined_terms = m_terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator-(const scalar_operator &other) const { + return *this + (-1.0 * other); +} + +operator_sum operator_sum::operator*=(const scalar_operator &other) { + *this = *this * other; + return *this; +} + +operator_sum operator_sum::operator+=(const scalar_operator &other) { + *this = *this + other; + return *this; +} + +operator_sum operator_sum::operator-=(const scalar_operator &other) { + *this = *this - other; + return *this; +} + +operator_sum operator_sum::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +operator_sum operator_sum::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +operator_sum operator_sum::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +operator_sum operator_sum::operator*=(std::complex other) { + *this *= scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator+=(std::complex other) { + *this += scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator-=(std::complex other) { + *this -= scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator*(double other) const { + return *this * scalar_operator(other); +} + +operator_sum operator_sum::operator+(double other) const { + return *this + scalar_operator(other); +} + +operator_sum operator_sum::operator-(double other) const { + return *this - scalar_operator(other); +} + +operator_sum operator_sum::operator*=(double other) { + *this *= scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator+=(double other) { + *this += scalar_operator(other); + return *this; +} + +operator_sum operator_sum::operator-=(double other) { + *this -= scalar_operator(other); + return *this; +} + +operator_sum operator*(std::complex other, operator_sum self) { + return scalar_operator(other) * self; +} + +operator_sum operator+(std::complex other, operator_sum self) { + return scalar_operator(other) + self; +} + +operator_sum operator-(std::complex other, operator_sum self) { + return scalar_operator(other) - self; +} + +operator_sum operator*(double other, operator_sum self) { + return scalar_operator(other) * self; +} + +operator_sum operator+(double other, operator_sum self) { + return scalar_operator(other) + self; +} + +operator_sum operator-(double other, operator_sum self) { + return scalar_operator(other) - self; +} + +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector combined_terms = m_terms; + combined_terms.push_back(other); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator+=(const product_operator &other) { + *this = *this + other; + return *this; +} + +operator_sum operator_sum::operator-(const product_operator &other) const { + return *this + (-1. * other); +} + +operator_sum operator_sum::operator-=(const product_operator &other) { + *this = *this - other; + return *this; +} + +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector combined_terms = m_terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator*=(const product_operator &other) { + *this = *this * other; + return *this; +} + +operator_sum operator_sum::operator+(const elementary_operator &other) const { + std::vector combined_terms = m_terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator-(const elementary_operator &other) const { + std::vector combined_terms = m_terms; + combined_terms.push_back((-1. * other)); + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator*(const elementary_operator &other) const { + std::vector combined_terms = m_terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +operator_sum operator_sum::operator+=(const elementary_operator &other) { + std::vector> _other = { + other}; + *this = *this + product_operator(_other); + return *this; +} + +operator_sum operator_sum::operator-=(const elementary_operator &other) { + std::vector> _other = { + other}; + *this = *this - product_operator(_other); + return *this; +} + +operator_sum operator_sum::operator*=(const elementary_operator &other) { + *this = *this * other; + return *this; +} + +/// FIXME: +// tensor +// operator_sum::to_matrix(const std::map &dimensions, +// const std::map ¶ms) const { +// // todo +// } + +// std::string operator_sum::to_string() const { +// std::string result; +// // for (const auto &term : m_terms) { +// // result += term.to_string() + " + "; +// // } +// // // Remove last " + " +// // if (!result.empty()) +// // result.pop_back(); +// return result; +// } + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp new file mode 100644 index 0000000000..0c8b411b1a --- /dev/null +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -0,0 +1,271 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include +#include +#include + +namespace cudaq { + +/// Product Operator constructors. +product_operator::product_operator( + std::vector> + atomic_operators) + : m_terms(atomic_operators) {} + +// tensor kroneckerHelper(std::vector &matrices) { +// // essentially we pass in the list of elementary operators to +// // this function -- with lowest degree being leftmost -- then it computes +// the +// // kronecker product of all of them. +// auto kronecker = [](tensor self, tensor other) { +// return self.kronecker(other); +// }; + +// return std::accumulate(begin(matrices), end(matrices), +// tensor::identity(1, 1), kronecker); +// } + +// /// IMPLEMENT: +// tensor product_operator::to_matrix( +// std::map dimensions, +// std::map> parameters) { + +// /// TODO: This initial logic may not be needed. +// // std::vector degrees, levels; +// // for(std::map::iterator it = dimensions.begin(); it != +// // dimensions.end(); ++it) { +// // degrees.push_back(it->first); +// // levels.push_back(it->second); +// // } +// // // Calculate the size of the full Hilbert space of the given product +// // operator. int fullSize = std::accumulate(begin(levels), end(levels), 1, +// // std::multiplies()); +// std::cout << "here 49\n"; +// auto getDegrees = [](auto &&t) { return t.degrees; }; +// auto getMatrix = [&](auto &&t) { +// auto outMatrix = t.to_matrix(dimensions, parameters); +// std::cout << "dumping the outMatrix : \n"; +// outMatrix.dump(); +// return outMatrix; +// }; +// std::vector matricesFullVectorSpace; +// for (auto &term : m_terms) { +// auto op_degrees = std::visit(getDegrees, term); +// std::cout << "here 58\n"; +// // Keeps track of if we've already inserted the operator matrix +// // into the full list of matrices. +// bool alreadyInserted = false; +// std::vector matrixWithIdentities; +// /// General procedure for inserting identities: +// // * check if the operator acts on this degree by looking through +// // `op_degrees` +// // * if not, insert an identity matrix of the proper level size +// // * if so, insert the matrix itself +// for (auto [degree, level] : dimensions) { +// std::cout << "here 68\n"; +// auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); +// if (it != op_degrees.end() && !alreadyInserted) { +// std::cout << "here 71\n"; +// auto matrix = std::visit(getMatrix, term); +// std::cout << "here 75\n"; +// matrixWithIdentities.push_back(matrix); +// std::cout << "here 77\n"; +// } else { +// std::cout << "here 80\n"; +// matrixWithIdentities.push_back(tensor::identity(level, +// level)); +// } +// } +// std::cout << "here 84\n"; +// matricesFullVectorSpace.push_back(kroneckerHelper(matrixWithIdentities)); +// } +// // Now just need to accumulate with matrix multiplication all of the +// // matrices in `matricesFullVectorSpace` -- they should all be the same +// size +// // already. +// std::cout << "here 89\n"; + +// // temporary +// auto out = tensor::identity(1, 1); +// std::cout << "here 93\n"; +// return out; +// } + +// Degrees property +std::vector product_operator::degrees() const { + std::set unique_degrees; + // The variant type makes it difficult + auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; + auto endFunc = [](auto &&t) { return t.degrees.end(); }; + for (const auto &term : m_terms) { + unique_degrees.insert(std::visit(beginFunc, term), + std::visit(endFunc, term)); + } + // Erase any `-1` degree values that may have come from scalar operators. + auto it = unique_degrees.find(-1); + if (it != unique_degrees.end()) { + unique_degrees.erase(it); + } + return std::vector(unique_degrees.begin(), unique_degrees.end()); +} + +operator_sum product_operator::operator+(scalar_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +operator_sum product_operator::operator-(scalar_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); +} + +product_operator product_operator::operator*(scalar_operator other) { + std::vector> + combined_terms = m_terms; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +product_operator product_operator::operator*=(scalar_operator other) { + *this = *this * other; + return *this; +} + +operator_sum product_operator::operator+(std::complex other) { + return *this + scalar_operator(other); +} + +operator_sum product_operator::operator-(std::complex other) { + return *this - scalar_operator(other); +} + +product_operator product_operator::operator*(std::complex other) { + return *this * scalar_operator(other); +} + +product_operator product_operator::operator*=(std::complex other) { + *this = *this * scalar_operator(other); + return *this; +} + +operator_sum operator+(std::complex other, product_operator self) { + return operator_sum({scalar_operator(other), self}); +} + +operator_sum operator-(std::complex other, product_operator self) { + return scalar_operator(other) - self; +} + +product_operator operator*(std::complex other, product_operator self) { + return scalar_operator(other) * self; +} + +operator_sum product_operator::operator+(double other) { + return *this + scalar_operator(other); +} + +operator_sum product_operator::operator-(double other) { + return *this - scalar_operator(other); +} + +product_operator product_operator::operator*(double other) { + return *this * scalar_operator(other); +} + +product_operator product_operator::operator*=(double other) { + *this = *this * scalar_operator(other); + return *this; +} + +operator_sum operator+(double other, product_operator self) { + return operator_sum({scalar_operator(other), self}); +} + +operator_sum operator-(double other, product_operator self) { + return scalar_operator(other) - self; +} + +product_operator operator*(double other, product_operator self) { + return scalar_operator(other) * self; +} + +operator_sum product_operator::operator+(product_operator other) { + return operator_sum({*this, other}); +} + +operator_sum product_operator::operator-(product_operator other) { + return operator_sum({*this, (-1. * other)}); +} + +product_operator product_operator::operator*(product_operator other) { + std::vector> + combined_terms = m_terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.m_terms.begin()), + std::make_move_iterator(other.m_terms.end())); + return product_operator(combined_terms); +} + +product_operator product_operator::operator*=(product_operator other) { + *this = *this * other; + return *this; +} + +operator_sum product_operator::operator+(elementary_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +operator_sum product_operator::operator-(elementary_operator other) { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); +} + +product_operator product_operator::operator*(elementary_operator other) { + std::vector> + combined_terms = m_terms; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +product_operator product_operator::operator*=(elementary_operator other) { + *this = *this * other; + return *this; +} + +operator_sum product_operator::operator+(operator_sum other) { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum product_operator::operator-(operator_sum other) { + auto negative_other = (-1. * other); + std::vector other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum product_operator::operator*(operator_sum other) { + std::vector other_terms = other.get_terms(); + for (auto &term : other_terms) { + term = *this * term; + } + return operator_sum(other_terms); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp new file mode 100644 index 0000000000..1be54ea9ee --- /dev/null +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -0,0 +1,275 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "common/EigenDense.h" +#include "cudaq/operators.h" + +#include +#include + +namespace cudaq { + +/// Constructors. +scalar_operator::scalar_operator(const scalar_operator &other) + : generator(other.generator), m_constant_value(other.m_constant_value) {} +scalar_operator::scalar_operator(scalar_operator &other) + : generator(other.generator), m_constant_value(other.m_constant_value) {} + +/// @brief Constructor that just takes and returns a complex double value. +scalar_operator::scalar_operator(std::complex value) { + m_constant_value = value; + auto func = [&](std::map> _none) { + return m_constant_value; + }; + generator = ScalarCallbackFunction(func); +} + +/// @brief Constructor that just takes a double and returns a complex double. +scalar_operator::scalar_operator(double value) { + std::complex castValue(value, 0.0); + m_constant_value = castValue; + auto func = [&](std::map> _none) { + return m_constant_value; + }; + generator = ScalarCallbackFunction(func); +} + +std::complex scalar_operator::evaluate( + std::map> parameters) { + return generator(parameters); +} + +matrix_2 scalar_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + auto returnOperator = matrix_2(1, 1); + returnOperator[{0, 0}] = evaluate(parameters); + return returnOperator; +} + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ + scalar_operator operator op(std::complex other, \ + scalar_operator self) { \ + /* Create an operator for the complex double value. */ \ + auto otherOperator = scalar_operator(other); \ + /* Create an operator that we will store the result in and return to the \ + * user. */ \ + scalar_operator returnOperator; \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + returnOperator._operators_to_compose.push_back(self); \ + returnOperator._operators_to_compose.push_back(otherOperator); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + /* FIXME: I have to use this hacky `.get_val()` on the newly created \ + * operator for the given complex double -- because calling the \ + * evaluate function returns 0.0 . I have no clue why??? */ \ + return returnOperator._operators_to_compose[0] \ + .evaluate(parameters) op returnOperator._operators_to_compose[1] \ + .get_val(); \ + }; \ + returnOperator.generator = ScalarCallbackFunction(newGenerator); \ + return returnOperator; \ + } + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(scalar_operator self, \ + std::complex other) { \ + /* Create an operator for the complex double value. */ \ + auto otherOperator = scalar_operator(other); \ + /* Create an operator that we will store the result in and return to the \ + * user. */ \ + scalar_operator returnOperator; \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + returnOperator._operators_to_compose.push_back(self); \ + returnOperator._operators_to_compose.push_back(otherOperator); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + /* FIXME: I have to use this hacky `.get_val()` on the newly created \ + * operator for the given complex double -- because calling the \ + * evaluate function returns 0.0 . I have no clue why??? */ \ + return returnOperator._operators_to_compose[1] \ + .get_val() op returnOperator._operators_to_compose[0] \ + .evaluate(parameters); \ + }; \ + returnOperator.generator = ScalarCallbackFunction(newGenerator); \ + return returnOperator; \ + } + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, std::complex other) { \ + /* Create an operator for the complex double value. */ \ + auto otherOperator = scalar_operator(other); \ + /* Need to move the existing generating function to a new operator so that \ + * we can modify the generator in `self` in-place. */ \ + scalar_operator copy(self); \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + self._operators_to_compose.push_back(copy); \ + self._operators_to_compose.push_back(otherOperator); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + /* FIXME: I have to use this hacky `.get_val()` on the newly created \ + * operator for the given complex double -- because calling the \ + * evaluate function returns 0.0 . I have no clue why??? */ \ + return self._operators_to_compose[0] \ + .evaluate(parameters) op self._operators_to_compose[1] \ + .get_val(); \ + }; \ + self.generator = ScalarCallbackFunction(newGenerator); \ + } + +#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ + scalar_operator operator op(double other, scalar_operator self) { \ + std::complex value(other, 0.0); \ + return self op value; \ + } + +#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ + scalar_operator operator op(scalar_operator self, double other) { \ + std::complex value(other, 0.0); \ + return value op self; \ + } + +#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, double other) { \ + std::complex value(other, 0.0); \ + self op value; \ + } + +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES(+); +ARITHMETIC_OPERATIONS_DOUBLES(-); +ARITHMETIC_OPERATIONS_DOUBLES(*); +ARITHMETIC_OPERATIONS_DOUBLES(/); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator scalar_operator::operator op(scalar_operator other) { \ + /* Create an operator that we will store the result in and return to the \ + * user. */ \ + scalar_operator returnOperator; \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + returnOperator._operators_to_compose.push_back(*this); \ + returnOperator._operators_to_compose.push_back(other); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return returnOperator._operators_to_compose[0] \ + .evaluate(parameters) op returnOperator._operators_to_compose[1] \ + .evaluate(parameters); \ + }; \ + returnOperator.generator = ScalarCallbackFunction(newGenerator); \ + return returnOperator; \ + } + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, scalar_operator other) { \ + /* Need to move the existing generating function to a new operator so \ + * that we can modify the generator in `self` in-place. */ \ + scalar_operator selfCopy(self); \ + /* Store the previous generator functions in the new operator. This is \ + * needed as the old generator functions would effectively be lost once we \ + * leave this function scope. */ \ + self._operators_to_compose.push_back(selfCopy); \ + self._operators_to_compose.push_back(other); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return self._operators_to_compose[0] \ + .evaluate(parameters) op self._operators_to_compose[1] \ + .evaluate(parameters); \ + }; \ + self.generator = ScalarCallbackFunction(newGenerator); \ + } + +ARITHMETIC_OPERATIONS_SCALAR_OPS(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS(-); +ARITHMETIC_OPERATIONS_SCALAR_OPS(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); + +operator_sum scalar_operator::operator+(elementary_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum({product_operator({*this}), product_operator({other})}); +} + +operator_sum scalar_operator::operator-(elementary_operator other) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum( + {product_operator({*this}), product_operator({-1. * other})}); +} + +product_operator scalar_operator::operator*(elementary_operator other) { + return product_operator({*this, other}); +} + +operator_sum scalar_operator::operator+(product_operator other) { + return operator_sum({product_operator({*this}), other}); +} + +operator_sum scalar_operator::operator-(product_operator other) { + return operator_sum({product_operator({*this}), (-1. * other)}); +} + +product_operator scalar_operator::operator*(product_operator other) { + std::vector> other_terms = + other.get_terms(); + /// Insert this scalar operator to the front of the terms list. + other_terms.insert(other_terms.begin(), *this); + return product_operator(other_terms); +} + +operator_sum scalar_operator::operator+(operator_sum other) { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum scalar_operator::operator-(operator_sum other) { + auto negative_other = (-1. * other); + std::vector other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +operator_sum scalar_operator::operator*(operator_sum other) { + std::vector other_terms = other.get_terms(); + for (auto &term : other_terms) + term = *this * term; + return operator_sum(other_terms); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/schedule.cpp b/runtime/cudaq/dynamics/schedule.cpp new file mode 100644 index 0000000000..6e833acb4f --- /dev/null +++ b/runtime/cudaq/dynamics/schedule.cpp @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/schedule.h" +#include +#include + +namespace cudaq { + +// Constructor +Schedule::Schedule(std::vector> steps, + std::vector parameters, + std::function( + const std::string &, const std::complex &)> + value_function) + : _steps(steps), _parameters(parameters), _value_function(value_function), + _current_idx(-1) { + if (!_steps.empty()) { + m_ptr = &_steps[0]; + } else { + m_ptr = nullptr; + } +} + +// Dereference operator +Schedule::reference Schedule::operator*() const { return *m_ptr; } + +// Arrow operator +Schedule::pointer Schedule::operator->() { return m_ptr; } + +// Prefix increment +Schedule &Schedule::operator++() { + if (_current_idx + 1 < static_cast(_steps.size())) { + _current_idx++; + m_ptr = &_steps[_current_idx]; + } else { + throw std::out_of_range("No more steps in the schedule."); + } + return *this; +} + +// Postfix increment +Schedule Schedule::operator++(int) { + Schedule tmp = *this; + ++(*this); + return tmp; +} + +// Comparison operators +bool operator==(const Schedule &a, const Schedule &b) { + return a.m_ptr == b.m_ptr; +}; + +bool operator!=(const Schedule &a, const Schedule &b) { + return a.m_ptr != b.m_ptr; +}; + +// Reset schedule +void Schedule::reset() { + _current_idx = -1; + if (!_steps.empty()) { + m_ptr = &_steps[0]; + } else { + m_ptr = nullptr; + } +} + +// Get the current step +std::optional> Schedule::current_step() const { + if (_current_idx >= 0 && _current_idx < static_cast(_steps.size())) { + return _steps[_current_idx]; + } + return std::nullopt; +} + +} // namespace cudaq diff --git a/runtime/cudaq/operator_utils.h b/runtime/cudaq/operator_utils.h new file mode 100644 index 0000000000..568cc8298e --- /dev/null +++ b/runtime/cudaq/operator_utils.h @@ -0,0 +1,40 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "definition.h" +#include "matrix.h" +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +inline std::map> +aggregate_parameters(const std::map ¶m1, + const std::map ¶m2) { + std::map> merged_map = param1; + + for (const auto &[key, value] : param2) { + /// FIXME: May just be able to remove this whole conditional block + /// since we're not dealing with std::string entries, but instead + /// complex doubles now. + if (merged_map.find(key) != merged_map.end()) { + // do nothing + } else { + merged_map[key] = value; + } + } + + return merged_map; +} +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h new file mode 100644 index 0000000000..77ee4703bf --- /dev/null +++ b/runtime/cudaq/operators.h @@ -0,0 +1,464 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "definition.h" +#include "utils/tensor.h" + +#include +#include +#include +#include + +namespace cudaq { + +class operator_sum; +class product_operator; +class scalar_operator; +class elementary_operator; + +/// @brief Represents an operator expression consisting of a sum of terms, where +/// each term is a product of elementary and scalar operators. Operator +/// expressions cannot be used within quantum kernels, but they provide methods +/// to convert them to data types that can. +class operator_sum { +private: + std::vector m_terms; + + std::vector> + canonicalize_product(product_operator &prod) const; + + std::vector> + _canonical_terms() const; + +public: + /// @brief Empty constructor that a user can aggregate terms into. + operator_sum() = default; + + /// @brief Construct a `cudaq::operator_sum` given a sequence of + /// `cudaq::product_operator`'s. + /// This operator expression represents a sum of terms, where each term + /// is a product of elementary and scalar operators. + operator_sum(const std::vector &terms); + + operator_sum canonicalize() const; + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + bool _is_spinop() const; + + /// TODO: implement + // template + // TEval _evaluate(OperatorArithmetics &arithmetics) const; + + /// @brief Return the `operator_sum` as a matrix. + /// @arg `dimensions` : A mapping that specifies the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0:2, 1:2}`. + /// @arg `parameters` : A map of the paramter names to their concrete, complex + /// values. + matrix_2 to_matrix(const std::map &dimensions, + const std::map ¶ms = {}) const; + + // Arithmetic operators + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; + operator_sum operator*(operator_sum &other) const; + operator_sum operator*=(operator_sum &other); + operator_sum operator+=(const operator_sum &other); + operator_sum operator-=(const operator_sum &other); + operator_sum operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + operator_sum operator*=(const scalar_operator &other); + operator_sum operator+=(const scalar_operator &other); + operator_sum operator-=(const scalar_operator &other); + operator_sum operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + operator_sum operator*=(std::complex other); + operator_sum operator+=(std::complex other); + operator_sum operator-=(std::complex other); + operator_sum operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + operator_sum operator*=(double other); + operator_sum operator+=(double other); + operator_sum operator-=(double other); + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + operator_sum operator*=(const product_operator &other); + operator_sum operator+=(const product_operator &other); + operator_sum operator-=(const product_operator &other); + operator_sum operator+(const elementary_operator &other) const; + operator_sum operator-(const elementary_operator &other) const; + operator_sum operator*(const elementary_operator &other) const; + operator_sum operator*=(const elementary_operator &other); + operator_sum operator+=(const elementary_operator &other); + operator_sum operator-=(const elementary_operator &other); + + /// @brief Return the operator_sum as a string. + std::string to_string() const; + + /// @brief Return the number of operator terms that make up this operator sum. + int term_count() const { return m_terms.size(); } + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms blockwise; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(const operator_sum &other) const; + + /// FIXME: Protect this once I can do deeper testing in unittests. + // protected: + std::vector get_terms() { return m_terms; } +}; +operator_sum operator*(std::complex other, operator_sum self); +operator_sum operator+(std::complex other, operator_sum self); +operator_sum operator-(std::complex other, operator_sum self); +operator_sum operator*(double other, operator_sum self); +operator_sum operator+(double other, operator_sum self); +operator_sum operator-(double other, operator_sum self); + +/// @brief Represents an operator expression consisting of a product of +/// elementary and scalar operators. Operator expressions cannot be used within +/// quantum kernels, but they provide methods to convert them to data types +/// that can. +class product_operator : public operator_sum { +private: + std::vector> m_terms; + +public: + product_operator() = default; + ~product_operator() = default; + + // Constructor for an operator expression that represents a product + // of scalar and elementary operators. + // arg atomic_operators : The operators of which to compute the product when + // evaluating the operator expression. + product_operator( + std::vector> + atomic_operators); + + // Arithmetic overloads against all other operator types. + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + product_operator operator*=(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + product_operator operator*=(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + product_operator operator*=(scalar_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + product_operator operator*=(product_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + product_operator operator*=(elementary_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms blockwise; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(product_operator other); + + /// @brief Return the `product_operator` as a string. + std::string to_string() const; + + /// @brief Return the `operator_sum` as a matrix. + /// @arg `dimensions` : A mapping that specifies the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0:2, 1:2}`. + /// @arg `parameters` : A map of the paramter names to their concrete, complex + /// values. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); + + /// @brief Creates a representation of the operator as a `cudaq::pauli_word` + /// that can be passed as an argument to quantum kernels. + // pauli_word to_pauli_word(); + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + /// @brief Return the number of operator terms that make up this product + /// operator. + int term_count() const { return m_terms.size(); } + + /// FIXME: Protect this once I can do deeper testing in unittests. + // protected: + std::vector> get_terms() { + return m_terms; + }; +}; +operator_sum operator+(std::complex other, product_operator self); +operator_sum operator-(std::complex other, product_operator self); +product_operator operator*(std::complex other, product_operator self); +operator_sum operator+(double other, product_operator self); +operator_sum operator-(double other, product_operator self); +product_operator operator*(double other, product_operator self); + +class elementary_operator : public product_operator { +private: + std::map m_ops; + +public: + // The constructor should never be called directly by the user: + // Keeping it internally documentd for now, however. + /// @brief Constructor. + /// @arg operator_id : The ID of the operator as specified when it was + /// defined. + /// @arg degrees : the degrees of freedom that the operator acts upon. + elementary_operator(std::string operator_id, std::vector degrees); + + // Copy constructor. + elementary_operator(const elementary_operator &other); + elementary_operator(elementary_operator &other); + + // Arithmetic overloads against all other operator types. + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator+=(operator_sum other); + operator_sum operator-=(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief True, if the other value is an elementary operator with the same id + /// acting on the same degrees of freedom, and False otherwise. + bool operator==(elementary_operator other); + + /// @brief Return the `elementary_operator` as a string. + std::string to_string() const; + + /// @brief Return the `elementary_operator` as a matrix. + /// @arg `dimensions` : A map specifying the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0 : 2, 1 : 2}`. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); + + // Predefined operators. + static elementary_operator identity(int degree); + static elementary_operator zero(int degree); + static elementary_operator annihilate(int degree); + static elementary_operator create(int degree); + static elementary_operator momentum(int degree); + static elementary_operator number(int degree); + static elementary_operator parity(int degree); + static elementary_operator position(int degree); + /// FIXME: + static elementary_operator squeeze(int degree, + std::complex amplitude); + static elementary_operator displace(int degree, + std::complex amplitude); + + /// @brief Adds the definition of an elementary operator with the given id to + /// the class. After definition, an the defined elementary operator can be + /// instantiated by providing the operator id as well as the degree(s) of + /// freedom that it acts on. An elementary operator is a parameterized object + /// acting on certain degrees of freedom. To evaluate an operator, for example + /// to compute its matrix, the level, that is the dimension, for each degree + /// of freedom it acts on must be provided, as well as all additional + /// parameters. Additional parameters must be provided in the form of keyword + /// arguments. Note: The dimensions passed during operator evaluation are + /// automatically validated against the expected dimensions specified during + /// definition - the `create` function does not need to do this. + /// @arg operator_id : A string that uniquely identifies the defined operator. + /// @arg expected_dimensions : Defines the number of levels, that is the + /// dimension, + /// for each degree of freedom in canonical (that is sorted) order. A + /// negative or zero value for one (or more) of the expected dimensions + /// indicates that the operator is defined for any dimension of the + /// corresponding degree of freedom. + /// @arg create : Takes any number of complex-valued arguments and returns the + /// matrix representing the operator in canonical order. If the matrix + /// can be defined for any number of levels for one or more degree of + /// freedom, the `create` function must take an argument called + /// `dimension` (or `dim` for short), if the operator acts on a single + /// degree of freedom, and an argument called `dimensions` (or `dims` for + /// short), if the operator acts + /// on multiple degrees of freedom. + template + void define(std::string operator_id, std::map expected_dimensions, + Func create) { + if (m_ops.find(operator_id) != m_ops.end()) { + // todo: make a nice error message to say op already exists + throw; + } + auto defn = Definition(); + defn.create_definition(operator_id, expected_dimensions, create); + m_ops[operator_id] = defn; + } + + // Attributes. + + /// @brief The number of levels, that is the dimension, for each degree of + /// freedom in canonical order that the operator acts on. A value of zero or + /// less indicates that the operator is defined for any dimension of that + /// degree. + std::map expected_dimensions; + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees; + std::string id; + + // /// @brief Creates a representation of the operator as `pauli_word` that + // can be passed as an argument to quantum kernels. + // pauli_word to_pauli_word ovveride(); +}; +// Reverse order arithmetic for elementary operators against pure scalars. +operator_sum operator+(std::complex other, elementary_operator self); +operator_sum operator-(std::complex other, elementary_operator self); +product_operator operator*(std::complex other, + elementary_operator self); +operator_sum operator+(double other, elementary_operator self); +operator_sum operator-(double other, elementary_operator self); +product_operator operator*(double other, elementary_operator self); + +class scalar_operator : public product_operator { +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::complex m_constant_value; + +public: + /// @brief Constructor that just takes a callback function with no + /// arguments. + + scalar_operator(ScalarCallbackFunction &&create) { + generator = ScalarCallbackFunction(create); + } + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value); + scalar_operator(double value); + + // Arithmetic overloads against other operator types. + scalar_operator operator+(scalar_operator other); + scalar_operator operator-(scalar_operator other); + scalar_operator operator*(scalar_operator other); + scalar_operator operator/(scalar_operator other); + /// TODO: implement and test pow + scalar_operator pow(scalar_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(std::map> parameters); + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatability with the other inherited classes. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); + + // /// @brief Returns true if other is a scalar operator with the same + // /// generator. + // bool operator==(scalar_operator other); + + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; + + // Only populated when we've performed arithmetic between various + // scalar operators. + std::vector _operators_to_compose; + + /// NOTE: We should revisit these constructors and remove any that have + /// become unecessary as the implementation improves. + scalar_operator() = default; + // Copy constructor. + scalar_operator(const scalar_operator &other); + scalar_operator(scalar_operator &other); + + ~scalar_operator() = default; + + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {-1}; + + // REMOVEME: just using this as a temporary patch: + std::complex get_val() { return m_constant_value; }; +}; + +scalar_operator operator+(scalar_operator self, std::complex other); +scalar_operator operator-(scalar_operator self, std::complex other); +scalar_operator operator*(scalar_operator self, std::complex other); +scalar_operator operator/(scalar_operator self, std::complex other); +scalar_operator operator+(std::complex other, scalar_operator self); +scalar_operator operator-(std::complex other, scalar_operator self); +scalar_operator operator*(std::complex other, scalar_operator self); +scalar_operator operator/(std::complex other, scalar_operator self); +scalar_operator operator+(scalar_operator self, double other); +scalar_operator operator-(scalar_operator self, double other); +scalar_operator operator*(scalar_operator self, double other); +scalar_operator operator/(scalar_operator self, double other); +scalar_operator operator+(double other, scalar_operator self); +scalar_operator operator-(double other, scalar_operator self); +scalar_operator operator*(double other, scalar_operator self); +scalar_operator operator/(double other, scalar_operator self); +void operator+=(scalar_operator &self, std::complex other); +void operator-=(scalar_operator &self, std::complex other); +void operator*=(scalar_operator &self, std::complex other); +void operator/=(scalar_operator &self, std::complex other); +void operator+=(scalar_operator &self, scalar_operator other); +void operator-=(scalar_operator &self, scalar_operator other); +void operator*=(scalar_operator &self, scalar_operator other); +void operator/=(scalar_operator &self, scalar_operator other); + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h new file mode 100644 index 0000000000..c9610cc75b --- /dev/null +++ b/runtime/cudaq/schedule.h @@ -0,0 +1,105 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +/// @brief Create a schedule for evaluating an operator expression at different +/// steps. +class Schedule { +public: + /// Iterator tags. May be superfluous. + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = std::complex; + using pointer = std::complex *; + using reference = std::complex &; + +private: + pointer m_ptr; + std::vector> _steps; + std::vector _parameters; + std::function(const std::string &, + const std::complex &)> + _value_function; + int _current_idx; + +public: + Schedule(pointer ptr) : m_ptr(ptr){}; + + /// @brief Constructor. + /// @arg steps: The sequence of steps in the schedule. Restricted to a vector + /// of complex values. + /// @arg parameters: A sequence of strings representing the parameter names of + /// an operator expression. + /// @arg value_function: A function that takes the name of a parameter as well + /// as an additional value ("step") of arbitrary type as argument and returns + /// the complex value for that parameter at the given step. + /// @details current_idx: Intializes the current index (_current_idx) to -1 to + /// indicate that iteration has not yet begun. Once iteration starts, + /// _current_idx will be used to track the position in the sequence of steps. + Schedule(const std::vector> steps, + const std::vector parameters, + std::function(const std::string &, + const std::complex &)> + value_function); + + /// Below, I define what I believe are the minimal necessary methods needed + /// for this to behave like an iterable. This should be revisited in the + /// implementation phase. + + // Pointers. + /// @brief Dereference operator to access the current step value. + /// @return Reference to current complex step value. + reference operator*() const; + + /// @brief Arrow operator to access the pointer the current step value. + /// @return Pointer to the current complex step value. + pointer operator->(); + + // Prefix increment. + /// @brief Prefix increment operator to move to the next step in the schedule. + /// @return Reference to the updated Schedule object. + Schedule &operator++(); + + // Postfix increment. + /// @brief Postfix increment operator to move to the next step in the + /// schedule. + /// @return Copy of the previous Schedule state. + Schedule operator++(int); + + // Comparison. + /// @brief Equality comparison operator. + /// @param a: First Schedule object. + /// @param b: Second Schedule object. + /// @return True if both schedules point to the same step, false otherwise + friend bool operator==(const Schedule &a, const Schedule &b); + + /// @brief Inequality comparison operator. + /// @param a: First Schedule object. + /// @param b: Second Schedule object. + /// @return True if both schedules point to different steps, false otherwise + friend bool operator!=(const Schedule &a, const Schedule &b); + + /// @brief Reset the schedule iterator to the beginning. + void reset(); + + /// @brief Get the current step in the schedule. + /// @return The current complex step value as an optional. If no valid step, + /// returns std::nullopt. + std::optional> current_step() const; +}; +} // namespace cudaq diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index d9f9099264..8287ab93de 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -38,6 +38,9 @@ class matrix_2 { using Dimensions = std::pair; matrix_2() = default; + matrix_2(std::size_t rows, std::size_t cols) + : dimensions(std::make_pair(rows, cols)), + data{new std::complex[rows * cols]} {} matrix_2(const matrix_2 &other) : dimensions{other.dimensions}, data{new std::complex[get_size(other.dimensions)]} { diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index fa1a9be06d..df4748f80d 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -44,6 +44,11 @@ set(CUDAQ_RUNTIME_TEST_SOURCES common/NoiseModelTester.cpp integration/tracer_tester.cpp integration/gate_library_tester.cpp + dynamics/scalar_ops_simple.cpp + dynamics/scalar_ops_arithmetic.cpp + dynamics/elementary_ops_simple.cpp + dynamics/elementary_ops_arithmetic.cpp + dynamics/product_operators_arithmetic.cpp ) # Make it so we can get function symbols @@ -258,6 +263,27 @@ target_link_libraries(test_spin gtest_main) gtest_discover_tests(test_spin) +# Create an executable for operators UnitTests +set(CUDAQ_OPERATOR_TEST_SOURCES + dynamics/operator_sum.cpp + dynamics/elementary_ops_simple.cpp + dynamics/elementary_ops_arithmetic.cpp + dynamics/scalar_ops_simple.cpp + dynamics/scalar_ops_arithmetic.cpp + dynamics/product_operators_arithmetic.cpp +) +add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_options(test_operators PRIVATE -Wl,--no-as-needed) +endif() +target_link_libraries(test_operators + PRIVATE + cudaq-spin + cudaq-operators + cudaq + gtest_main) +gtest_discover_tests(test_operators) + add_subdirectory(plugin) # build the test qudit execution manager diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp new file mode 100644 index 0000000000..fb5c85ac49 --- /dev/null +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -0,0 +1,604 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include + +/// STATUS: +/// 1. I've generated all of the `want` matrices for each test, and prepared +/// the test to check against the `got` matrix. Now waiting on finishing the +/// full `to_matrix` conversion to be able to do so. +/// + +namespace utils_0 { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils_0 + +TEST(ExpressionTester, checkPreBuiltElementaryOpsScalars) { + + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + /// Keeping these fixed for these more simple tests. + int level_count = 3; + int degree_index = 0; + double const_scale_factor = 2.0; + + // `elementary_operator + scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + // Produces an `operator_sum` type. + auto sum = self + other; + auto reverse = other + self; + + // Check the `operator_sum` attributes. + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}, {}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator + scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + // Produces an `operator_sum` type. + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator - scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + // Produces an `operator_sum` type. + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}, {}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = + scaled_identity - utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator - scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + // Produces an `operator_sum` type. + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator * scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + // Produces an `product_operator` type. + auto product = self * other; + auto reverse = other * self; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = product.to_matrix({{degree_index, level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}, {}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) * scaled_identity; + auto want_reverse_matrix = + scaled_identity * utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } + + // `elementary_operator * scalar_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + // Produces an `product_operator` type. + auto product = self * other; + auto reverse = other * self; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); + // auto got_matrix = product.to_matrix({{degree_index, level_count}}, + // {"value", const_scale_factor}); auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {"value", + // const_scale_factor}); + auto want_matrix = + utils_0::annihilate_matrix(level_count) * scaled_identity; + auto want_reverse_matrix = + scaled_identity * utils_0::annihilate_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + } +} + +/// Prebuilt elementary ops against one another. +TEST(ExpressionTester, checkPreBuiltElementaryOpsSelf) { + + /// Keeping this fixed throughout. + int level_count = 3; + + // Addition, same DOF. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(0); + + // Produces an `operator_sum` type. + auto sum = self + other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = utils_0::annihilate_matrix(level_count) + + utils_0::create_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Addition, different DOF's. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(1); + + // Produces an `operator_sum` type. + auto sum = self + other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto annihilate_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), + utils_0::id_matrix(level_count)); + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = annihilate_full + create_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, same DOF. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(0); + + // Produces an `operator_sum` type. + auto sum = self - other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = utils_0::annihilate_matrix(level_count) - + utils_0::create_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, different DOF's. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(1); + + // Produces an `operator_sum` type. + auto sum = self - other; + ASSERT_TRUE(sum.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto annihilate_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), + utils_0::id_matrix(level_count)); + // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto want_matrix = annihilate_full - create_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, same DOF. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(0); + + // Produces an `product_operator` type. + auto product = self * other; + ASSERT_TRUE(product.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + auto want_matrix = utils_0::annihilate_matrix(level_count) * + utils_0::create_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, different DOF's. + { + auto self = cudaq::elementary_operator::annihilate(0); + auto other = cudaq::elementary_operator::create(1); + + // Produces an `product_operator` type. + auto product = self * other; + ASSERT_TRUE(product.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto annihilate_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), + utils_0::id_matrix(level_count)); + // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + auto want_matrix = annihilate_full * create_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } +} + +/// Testing arithmetic between elementary operators and operator +/// sums. +TEST(ExpressionTester, checkElementaryOpsAgainstOpSum) { + + /// Keeping this fixed throughout. + int level_count = 3; + + /// `elementary_operator + operator_sum` and `operator_sum + + /// elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + auto got = self + operator_sum; + auto reverse = operator_sum + self; + + ASSERT_TRUE(got.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = self_full + term_0_full + term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full + self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `elementary_operator - operator_sum` and `operator_sum - + /// elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + auto got = self - operator_sum; + auto reverse = operator_sum - self; + + ASSERT_TRUE(got.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = self_full - term_0_full - term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full - self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `elementary_operator * operator_sum` and `operator_sum * + /// elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + auto got = self * operator_sum; + auto reverse = operator_sum * self; + + ASSERT_TRUE(got.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto &term : got.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + + for (auto &term : reverse.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + auto sum_full = term_0_full + term_1_full; + + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = self_full * sum_full; + auto want_reverse_matrix = sum_full * self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `operator_sum += elementary_operator` + { + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + operator_sum += cudaq::elementary_operator::annihilate(0); + + ASSERT_TRUE(operator_sum.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = term_0_full + term_1_full + self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum -= elementary_operator` + { + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + operator_sum -= cudaq::elementary_operator::annihilate(0); + + ASSERT_TRUE(operator_sum.term_count() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + + // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = term_0_full + term_1_full - self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum *= elementary_operator` + { + auto self = cudaq::elementary_operator::annihilate(0); + /// Creating an arbitrary operator sum to work against. + auto operator_sum = cudaq::elementary_operator::create(0) + + cudaq::elementary_operator::identity(1); + + operator_sum *= self; + + ASSERT_TRUE(operator_sum.term_count() == 2); + + for (auto &term : operator_sum.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::id_matrix(level_count)); + auto sum_full = term_0_full + term_1_full; + + // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, + // level_count}}, {}); + auto want_matrix = sum_full * self_full; + // utils_0::checkEqual(want_matrix, got_matrix); + } +} diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp new file mode 100644 index 0000000000..172beeffe8 --- /dev/null +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include + +namespace utils { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils + +TEST(ExpressionTester, checkPreBuiltElementaryOps) { + std::vector levels = {2, 3, 4, 5}; + + // Keeping this fixed throughout. + int degree_index = 0; + + // Identity operator. + { + for (auto level_count : levels) { + // cudaq::operators::identity(int degree) + auto id = cudaq::elementary_operator::identity(degree_index); + auto got_id = id.to_matrix({{degree_index, level_count}}, {}); + auto want_id = utils::id_matrix(level_count); + utils::checkEqual(want_id, got_id); + } + } + + // Zero operator. + { + for (auto level_count : levels) { + auto zero = cudaq::elementary_operator::zero(degree_index); + auto got_zero = zero.to_matrix({{degree_index, level_count}}, {}); + auto want_zero = utils::zero_matrix(level_count); + utils::checkEqual(want_zero, got_zero); + } + } + + // Annihilation operator. + { + for (auto level_count : levels) { + auto annihilate = cudaq::elementary_operator::annihilate(degree_index); + auto got_annihilate = + annihilate.to_matrix({{degree_index, level_count}}, {}); + auto want_annihilate = utils::annihilate_matrix(level_count); + utils::checkEqual(want_annihilate, got_annihilate); + } + } + + // Creation operator. + { + for (auto level_count : levels) { + auto create = cudaq::elementary_operator::create(degree_index); + auto got_create = create.to_matrix({{degree_index, level_count}}, {}); + auto want_create = utils::create_matrix(level_count); + utils::checkEqual(want_create, got_create); + } + } + + // Position operator. + { + for (auto level_count : levels) { + auto position = cudaq::elementary_operator::position(degree_index); + auto got_position = position.to_matrix({{degree_index, level_count}}, {}); + auto want_position = utils::position_matrix(level_count); + utils::checkEqual(want_position, got_position); + } + } + + // Momentum operator. + { + for (auto level_count : levels) { + auto momentum = cudaq::elementary_operator::momentum(degree_index); + auto got_momentum = momentum.to_matrix({{degree_index, level_count}}, {}); + auto want_momentum = utils::momentum_matrix(level_count); + utils::checkEqual(want_momentum, got_momentum); + } + } + + // Number operator. + { + for (auto level_count : levels) { + auto number = cudaq::elementary_operator::number(degree_index); + auto got_number = number.to_matrix({{degree_index, level_count}}, {}); + auto want_number = utils::number_matrix(level_count); + utils::checkEqual(want_number, got_number); + } + } + + // Parity operator. + { + for (auto level_count : levels) { + auto parity = cudaq::elementary_operator::parity(degree_index); + auto got_parity = parity.to_matrix({{degree_index, level_count}}, {}); + auto want_parity = utils::parity_matrix(level_count); + utils::checkEqual(want_parity, got_parity); + } + } + + // // // Displacement operator. + // // { + // // for (auto level_count : levels) { + // // auto amplitude = 1.0 + 1.0j; + // // auto displace = cudaq::elementary_operator::displace(degree_index, + // // amplitude); auto got_displace = + // utils::displace.to_matrix({{degree_index, + // // level_count}}, {}); auto want_displace = + // displace_matrix(level_count, + // // amplitude); utils::checkEqual(want_displace, got_displace); + // // } + // // } + + // TODO: Squeeze operator. +} + +// TEST(ExpressionTester, checkCustomElementaryOps) { +// // pass +// } \ No newline at end of file diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp new file mode 100644 index 0000000000..c68779ef45 --- /dev/null +++ b/unittests/dynamics/operator_sum.cpp @@ -0,0 +1,572 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +namespace utils_2 { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils_2 + +// TEST(ExpressionTester, checkProductOperatorSimple) { +// std::vector levels = {2, 3, 4}; + +// // std::set uniqueDegrees; +// // std::copy(this->degrees.begin(), this->degrees.end(), +// // std::inserter(uniqueDegrees, uniqueDegrees.begin())); +// // std::copy(other.degrees.begin(), other.degrees.end(), +// // std::inserter(uniqueDegrees, uniqueDegrees.begin())); + +// // Arithmetic only between elementary operators with +// // same number of levels. +// { +// // Same degrees of freedom. +// { +// for (auto level_count : levels) { +// auto op0 = cudaq::elementary_operator::annihilate(0); +// auto op1 = cudaq::elementary_operator::create(0); + +// cudaq::product_operator got = op0 * op1; +// auto got_matrix = got.to_matrix({{0, level_count}}, {}); + +// auto matrix0 = _annihilate_matrix(level_count); +// auto matrix1 = _create_matrix(level_count); +// auto want_matrix = matrix0 * matrix1; + +// // ASSERT_TRUE(want_matrix == got_matrix); +// } +// } + +// // // Different degrees of freedom. +// // { +// // for (auto level_count : levels) { +// // auto op0 = cudaq::elementary_operator::annihilate(0); +// // auto op1 = cudaq::elementary_operator::create(1); + +// // cudaq::product_operator got = op0 * op1; +// // auto got_matrix = +// // got.to_matrix({{0, level_count}, {1, level_count}}, {}); + +// // cudaq::product_operator got_reverse = op1 * op0; +// // auto got_matrix_reverse = +// // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, +// {}); + +// // auto identity = _id_matrix(level_count); +// // auto matrix0 = _annihilate_matrix(level_count); +// // auto matrix1 = _create_matrix(level_count); + +// // auto fullHilbert0 = identity.kronecker(matrix0); +// // auto fullHilbert1 = matrix1.kronecker(identity); +// // auto want_matrix = fullHilbert0 * fullHilbert1; +// // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + +// // // ASSERT_TRUE(want_matrix == got_matrix); +// // // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); +// // } +// // } + +// // // Different degrees of freedom, non-consecutive. +// // { +// // for (auto level_count : levels) { +// // auto op0 = cudaq::elementary_operator::annihilate(0); +// // auto op1 = cudaq::elementary_operator::create(2); + +// // // cudaq::product_operator got = op0 * op1; +// // // auto got_matrix = +// got.to_matrix({{0,level_count},{2,level_count}}, +// // // {}); +// // } +// // } + +// // // Different degrees of freedom, non-consecutive but all dimensions +// // // provided. +// // { +// // for (auto level_count : levels) { +// // auto op0 = cudaq::elementary_operator::annihilate(0); +// // auto op1 = cudaq::elementary_operator::create(2); + +// // // cudaq::product_operator got = op0 * op1; +// // // auto got_matrix = +// // // +// got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, +// // {}); +// // } +// // } +// } +// } + +// TEST(ExpressionTester, checkProductOperatorSimple) { + +// std::complex value_0 = 0.1 + 0.1; +// std::complex value_1 = 0.1 + 1.0; +// std::complex value_2 = 2.0 + 0.1; +// std::complex value_3 = 2.0 + 1.0; + +// auto local_variable = true; +// auto function = [&](std::map> parameters) +// { +// if (!local_variable) +// throw std::runtime_error("Local variable not detected."); +// return parameters["value"]; +// }; + +// // Scalar Ops against Elementary Ops +// { +// // Identity against constant. +// { +// auto id_op = cudaq::elementary_operator::identity(0); +// auto scalar_op = cudaq::scalar_operator(value_0); + +// // auto multiplication = scalar_op * id_op; +// // auto addition = scalar_op + id_op; +// // auto subtraction = scalar_op - id_op; +// } + +// // Identity against constant from lambda. +// { +// auto id_op = cudaq::elementary_operator::identity(0); +// auto scalar_op = cudaq::scalar_operator(function); + +// // auto multiplication = scalar_op * id_op; +// // auto addition = scalar_op + id_op; +// // auto subtraction = scalar_op - id_op; +// } +// } +// } + +TEST(ExpressionTester, checkOperatorSumAgainstScalarOperator) { + + // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = sum * cudaq::scalar_operator(1.0); + auto reverse = cudaq::scalar_operator(1.0) * sum; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = original + cudaq::scalar_operator(1.0); + auto reverse = cudaq::scalar_operator(1.0) + original; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = original - cudaq::scalar_operator(1.0); + auto reverse = cudaq::scalar_operator(1.0) - original; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum *= scalar_operator` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= cudaq::scalar_operator(1.0); + + ASSERT_TRUE(sum.term_count() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum += scalar_operator` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += cudaq::scalar_operator(1.0); + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= scalar_operator` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= cudaq::scalar_operator(1.0); + + ASSERT_TRUE(sum.term_count() == 3); + } +} + +TEST(ExpressionTester, checkOperatorSumAgainstScalars) { + std::complex value = 0.1 + 0.1; + + // `operator_sum * double` and `double * operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = sum * 2.0; + auto reverse = 2.0 * sum; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum + double` and `double + operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = original + 2.0; + auto reverse = 2.0 + original; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum - double` and `double - operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = original - 2.0; + auto reverse = 2.0 - original; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum *= double` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= 2.0; + + ASSERT_TRUE(sum.term_count() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum += double` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += 2.0; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= double` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= 2.0; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum * std::complex` and `std::complex * + // operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = sum * value; + auto reverse = value * sum; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum + std::complex` and `std::complex + + // operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = original + value; + auto reverse = value + original; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum - std::complex` and `std::complex - + // operator_sum` + { + auto original = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = original - value; + auto reverse = value - original; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `operator_sum *= std::complex` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= value; + + ASSERT_TRUE(sum.term_count() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 2); + } + } + + // `operator_sum += std::complex` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += value; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= std::complex` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= value; + + ASSERT_TRUE(sum.term_count() == 3); + } +} + +TEST(ExpressionTester, checkOperatorSumAgainstOperatorSum) { + // `operator_sum + operator_sum` + { + auto sum_0 = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(3); + + auto sum = sum_0 + sum_1; + + ASSERT_TRUE(sum.term_count() == 5); + } + + // `operator_sum - operator_sum` + { + auto sum_0 = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(2); + + auto difference = sum_0 - sum_1; + + ASSERT_TRUE(difference.term_count() == 5); + } + + // `operator_sum * operator_sum` + { + auto sum_0 = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(2); + + auto sum_product = sum_0 * sum_1; + + ASSERT_TRUE(sum_product.term_count() == 6); + for (auto term : sum_product.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + } + + // `operator_sum *= operator_sum` + { + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::annihilate(1) + + cudaq::elementary_operator::create(2); + + sum *= sum_1; + + ASSERT_TRUE(sum.term_count() == 6); + for (auto term : sum.get_terms()) + ASSERT_TRUE(term.term_count() == 2); + } +} + +/// NOTE: Much of the simpler arithmetic between the two is tested in the +/// product operator test file. This mainly just tests the assignment operators +/// between the two types. +TEST(ExpressionTester, checkOperatorSumAgainstProduct) { + // `operator_sum += product_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum += product; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum -= product_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum -= product; + + ASSERT_TRUE(sum.term_count() == 3); + } + + // `operator_sum *= product_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + sum *= product; + + ASSERT_TRUE(sum.term_count() == 2); + + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.term_count() == 3); + } + } +} diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp new file mode 100644 index 0000000000..f8b6be7742 --- /dev/null +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -0,0 +1,591 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +#include + +namespace utils_1 { +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_val = a[{i, j}].real(); + double b_val = b[{i, j}].real(); + EXPECT_NEAR(a_val, b_val, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +// cudaq::matrix_2 displace_matrix(std::size_t size, +// std::complex amplitude) { +// auto mat = cudaq::matrix_2(size, size); +// for (std::size_t i = 0; i + 1 < size; i++) { +// mat[{i + 1, i}] = +// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; +// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * +// std::sqrt(static_cast(i + 1)) + +// 0.0 * 'j'; +// } +// return mat.exp(); +// } + +} // namespace utils_1 + +/// TODO: Not yet testing the output matrices coming from this arithmetic. + +TEST(ExpressionTester, checkProductOperatorSimpleMatrixChecks) { + std::vector levels = {2, 3, 4}; + + { + // Same degrees of freedom. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(0); + + cudaq::product_operator got = op0 * op1; + + // auto got_matrix = got.to_matrix({{0, level_count}}, {}); + + // auto matrix0 = _annihilate_matrix(level_count); + // auto matrix1 = _create_matrix(level_count); + // auto want_matrix = matrix0 * matrix1; + + // ASSERT_TRUE(want_matrix == got_matrix); + + std::vector want_degrees = {0}; + ASSERT_TRUE(got.degrees() == want_degrees); + } + } + + // Different degrees of freedom. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(1); + + cudaq::product_operator got = op0 * op1; + // auto got_matrix = + // got.to_matrix({{0, level_count}, {1, level_count}}, {}); + + cudaq::product_operator got_reverse = op1 * op0; + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); + + // auto identity = _id_matrix(level_count); + // auto matrix0 = _annihilate_matrix(level_count); + // auto matrix1 = _create_matrix(level_count); + + // auto fullHilbert0 = identity.kronecker(matrix0); + // auto fullHilbert1 = matrix1.kronecker(identity); + // auto want_matrix = fullHilbert0 * fullHilbert1; + // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // ASSERT_TRUE(want_matrix == got_matrix); + // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); + + std::vector want_degrees = {0, 1}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } + + // Different degrees of freedom, non-consecutive. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(2); + + cudaq::product_operator got = op0 * op1; + // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, + // {}); + + cudaq::product_operator got_reverse = op1 * op0; + + std::vector want_degrees = {0, 2}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } + + // Different degrees of freedom, non-consecutive but all dimensions + // provided. + { + for (auto level_count : levels) { + auto op0 = cudaq::elementary_operator::annihilate(0); + auto op1 = cudaq::elementary_operator::create(2); + + cudaq::product_operator got = op0 * op1; + // auto got_matrix = + // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); + + cudaq::product_operator got_reverse = op1 * op0; + + std::vector want_degrees = {0, 2}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } + } +} + +TEST(ExpressionTester, checkProductOperatorSimpleContinued) { + + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + + // Scalar Ops against Elementary Ops + { + // Annihilation against constant. + { + auto id_op = cudaq::elementary_operator::annihilate(0); + auto scalar_op = cudaq::scalar_operator(value_0); + + auto got = scalar_op * id_op; + auto got_reverse = scalar_op * id_op; + + std::vector want_degrees = {0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + + // Annihilation against constant from lambda. + { + auto id_op = cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(function); + + auto got = scalar_op * id_op; + auto got_reverse = scalar_op * id_op; + + std::vector want_degrees = {1}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + } + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstScalars) { + std::complex value_0 = 0.1 + 0.1; + + /// `product_operator + complex` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto sum = value_0 + product_op; + auto reverse = product_op + value_0; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator + double` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto sum = 2.0 + product_op; + auto reverse = product_op + 2.0; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator + scalar_operator` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + auto sum = scalar_op + product_op; + auto reverse = product_op + scalar_op; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator - complex` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto difference = value_0 - product_op; + auto reverse = product_op - value_0; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(difference.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator - double` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto difference = 2.0 - product_op; + auto reverse = product_op - 2.0; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(difference.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator - scalar_operator` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + auto difference = scalar_op - product_op; + auto reverse = product_op - scalar_op; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(difference.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator * complex` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto product = value_0 * product_op; + auto reverse = product_op * value_0; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator * double` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + + auto product = 2.0 * product_op; + auto reverse = product_op * 2.0; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator * scalar_operator` + { + auto product_op = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + auto product = scalar_op * product_op; + auto reverse = product_op * scalar_op; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); + } + + /// `product_operator *= complex` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + product *= value_0; + + ASSERT_TRUE(product.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + } + + /// `product_operator *= double` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + product *= 2.0; + + ASSERT_TRUE(product.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + } + + /// `product_operator *= scalar_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(1.0); + + product *= scalar_op; + + ASSERT_TRUE(product.term_count() == 3); + + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstProduct) { + + // `product_operator + product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + auto sum = term_0 + term_1; + + ASSERT_TRUE(sum.term_count() == 2); + } + + // `product_operator - product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + auto difference = term_0 - term_1; + + ASSERT_TRUE(difference.term_count() == 2); + } + + // `product_operator * product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + auto product = term_0 * term_1; + + ASSERT_TRUE(product.term_count() == 4); + } + + // `product_operator *= product_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto term_1 = cudaq::elementary_operator::create(1) * + cudaq::elementary_operator::annihilate(2); + + term_0 *= term_1; + + ASSERT_TRUE(term_0.term_count() == 4); + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstElementary) { + + // `product_operator + elementary_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + auto sum = product + elementary; + auto reverse = elementary + product; + + ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + } + + // `product_operator - elementary_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + auto difference = product - elementary; + auto reverse = elementary - product; + + ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + } + + // `product_operator * elementary_operator` + { + auto term_0 = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + auto product = term_0 * elementary; + auto reverse = elementary * term_0; + + ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `product_operator *= elementary_operator` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto elementary = cudaq::elementary_operator::create(1); + + product *= elementary; + + ASSERT_TRUE(product.term_count() == 3); + } +} + +TEST(ExpressionTester, checkProductOperatorAgainstOperatorSum) { + + // `product_operator + operator_sum` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto original_sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto sum = product + original_sum; + auto reverse = original_sum + product; + + ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `product_operator - operator_sum` + { + auto product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto original_sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto difference = product - original_sum; + auto reverse = original_sum - product; + + ASSERT_TRUE(difference.term_count() == 3); + ASSERT_TRUE(reverse.term_count() == 3); + } + + // `product_operator * operator_sum` + { + auto original_product = cudaq::elementary_operator::annihilate(0) * + cudaq::elementary_operator::annihilate(1); + auto sum = cudaq::elementary_operator::create(1) + + cudaq::elementary_operator::create(2); + + auto product = original_product * sum; + auto reverse = sum * original_product; + + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.term_count() == 3); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.term_count() == 3); + } + } +} diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp new file mode 100644 index 0000000000..a8d6054a32 --- /dev/null +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -0,0 +1,505 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +TEST(ExpressionTester, checkScalarOpsArithmeticDoubles) { + // Arithmetic overloads against complex doubles. + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + + // + : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + + auto new_scalar_op = value_1 + scalar_op; + // function + scalar_op; + auto reverse_order_op = scalar_op + value_1; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_1 + value_0; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op + reverse_order_op; + auto got_value_third = third_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value + want_value), + 1e-5); + } + + // + : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_0 + scalar_op; + auto reverse_order_op = scalar_op + value_0; + + auto got_value = new_scalar_op.evaluate({{"value", value_1}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 + value_0), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op + reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_1}}); + auto want_value = value_0 + value_1 + value_1 + value_0; + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // - : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_1); + + auto new_scalar_op = value_3 - scalar_op; + auto reverse_order_op = scalar_op - value_3; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op - reverse_order_op; + auto got_value_third = third_op.evaluate({}); + auto want_value = (value_3 - value_1) - (value_1 - value_3); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // - : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_2 - scalar_op; + auto reverse_order_op = scalar_op - value_2; + + auto got_value = new_scalar_op.evaluate({{"value", value_1}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 - value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_2), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op - reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_1}}); + auto want_value = (value_2 - value_1) - (value_1 - value_2); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // * : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + + auto new_scalar_op = value_3 * scalar_op; + auto reverse_order_op = scalar_op * value_3; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op * reverse_order_op; + auto got_value_third = third_op.evaluate({}); + auto want_value = (value_3 * value_2) * (value_2 * value_3); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // * : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_3 * scalar_op; + auto reverse_order_op = scalar_op * value_3; + + auto got_value = new_scalar_op.evaluate({{"value", value_2}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_2}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op * reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_2}}); + auto want_value = (value_3 * value_2) * (value_2 * value_3); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // / : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + + auto new_scalar_op = value_3 / scalar_op; + auto reverse_order_op = scalar_op / value_3; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_2), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op / reverse_order_op; + auto got_value_third = third_op.evaluate({}); + auto want_value = (value_2 / value_3) / (value_3 / value_2); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // / : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + + auto new_scalar_op = value_3 / scalar_op; + auto reverse_order_op = scalar_op / value_3; + + auto got_value = new_scalar_op.evaluate({{"value", value_1}}); + auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_1 / value_3), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_1), 1e-5); + + // Checking composition of many scalar operators. + auto third_op = new_scalar_op / reverse_order_op; + auto got_value_third = third_op.evaluate({{"value", value_1}}); + auto want_value = (value_1 / value_3) / (value_3 / value_1); + EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); + } + + // += : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + scalar_op += value_0; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); + } + + // += : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op += value_1; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + } + + // -= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + scalar_op -= value_0; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); + } + + // -= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op -= value_1; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_1), 1e-5); + } + + // *= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // *= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // /= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } + + // /= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } +} + +TEST(ExpressionTester, checkScalarOpsArithmeticScalarOps) { + // Arithmetic overloads against other scalar ops. + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + + // I use another function here to make sure that local variables + // that may be unique to each ScalarOp's generators are both kept + // track of when we merge the generators. + auto alternative_local_variable = true; + auto alternative_function = + [&](std::map> parameters) { + if (!alternative_local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["other"]; + }; + + // + : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + auto other_scalar_op = cudaq::scalar_operator(value_1); + + auto new_scalar_op = other_scalar_op + scalar_op; + auto reverse_order_op = scalar_op + other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_1 + value_0; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); + } + + // + : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op + scalar_op; + auto reverse_order_op = scalar_op + other_scalar_op; + + std::map> parameter_map = { + {"value", value_1}, {"other", value_0}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 + value_0), 1e-5); + } + + // - : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + auto other_scalar_op = cudaq::scalar_operator(value_1); + + auto new_scalar_op = other_scalar_op - scalar_op; + auto reverse_order_op = scalar_op - other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_1 - value_2; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); + } + + // - : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op - scalar_op; + auto reverse_order_op = scalar_op - other_scalar_op; + + std::map> parameter_map = { + {"value", value_1}, {"other", value_3}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); + } + + // * : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + auto other_scalar_op = cudaq::scalar_operator(value_3); + + auto new_scalar_op = other_scalar_op * scalar_op; + auto reverse_order_op = scalar_op * other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_3 * value_2; + auto reverse_want_value = value_2 * value_3; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(reverse_want_value), 1e-5); + } + + // * : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op * scalar_op; + auto reverse_order_op = scalar_op * other_scalar_op; + + std::map> parameter_map = { + {"value", value_1}, {"other", value_3}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 * value_3), 1e-5); + } + + // / : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + auto other_scalar_op = cudaq::scalar_operator(value_2); + + auto new_scalar_op = other_scalar_op / scalar_op; + auto reverse_order_op = scalar_op / other_scalar_op; + + auto got_value = new_scalar_op.evaluate({}); + auto got_value_1 = reverse_order_op.evaluate({}); + auto want_value = value_2 / value_0; + auto reverse_want_value = value_0 / value_2; + + EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(reverse_want_value), 1e-5); + } + + // / : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other_scalar_op = cudaq::scalar_operator(alternative_function); + + auto new_scalar_op = other_scalar_op / scalar_op; + auto reverse_order_op = scalar_op / other_scalar_op; + + std::map> parameter_map = { + {"value", value_0}, {"other", value_3}}; + + auto got_value = new_scalar_op.evaluate(parameter_map); + auto got_value_1 = reverse_order_op.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_0), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_0 / value_3), 1e-5); + } + + // += : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + auto other = cudaq::scalar_operator(value_0); + scalar_op += other; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); + } + + // += : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + auto other = cudaq::scalar_operator(value_1); + scalar_op += other; + + auto scalar_op_1 = cudaq::scalar_operator(function); + auto other_function = cudaq::scalar_operator(alternative_function); + scalar_op_1 += other_function; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + auto got_value_1 = + scalar_op_1.evaluate({{"value", value_0}, {"other", value_1}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_0 + value_1), 1e-5); + } + + // -= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_0); + scalar_op -= value_0; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); + } + + // -= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op -= value_1; + + auto got_value = scalar_op.evaluate({{"value", value_0}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_1), 1e-5); + } + + // *= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // *= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op *= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); + } + + // /= : Constant scalar operator. + { + auto scalar_op = cudaq::scalar_operator(value_2); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } + + // /= : Scalar operator from lambda. + { + auto scalar_op = cudaq::scalar_operator(function); + scalar_op /= value_3; + + auto got_value = scalar_op.evaluate({{"value", value_2}}); + EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); + } +} \ No newline at end of file diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp new file mode 100644 index 0000000000..c60414d705 --- /dev/null +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/matrix.h" +#include "cudaq/operators.h" +#include + +TEST(ExpressionTester, checkScalarOpsSimpleComplex) { + + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + // From concrete values. + { + auto operator_0 = cudaq::scalar_operator(value_0); + auto operator_1 = cudaq::scalar_operator(value_1); + auto operator_2 = cudaq::scalar_operator(value_2); + auto operator_3 = cudaq::scalar_operator(value_3); + + auto got_value_0 = operator_0.evaluate({}); + auto got_value_1 = operator_1.evaluate({}); + auto got_value_2 = operator_2.evaluate({}); + auto got_value_3 = operator_3.evaluate({}); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } + + // From a lambda function. + { + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + std::map> parameter_map; + + auto operator_0 = cudaq::scalar_operator(function); + auto operator_1 = cudaq::scalar_operator(function); + auto operator_2 = cudaq::scalar_operator(function); + auto operator_3 = cudaq::scalar_operator(function); + + parameter_map["value"] = value_0; + auto got_value_0 = operator_0.evaluate(parameter_map); + parameter_map["value"] = value_1; + auto got_value_1 = operator_1.evaluate(parameter_map); + parameter_map["value"] = value_2; + auto got_value_2 = operator_2.evaluate(parameter_map); + parameter_map["value"] = value_3; + auto got_value_3 = operator_3.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } +} + +TEST(ExpressionTester, checkScalarOpsSimpleDouble) { + + double value_0 = 0.1; + double value_1 = 0.2; + double value_2 = 2.1; + double value_3 = 2.2; + + // From concrete values. + { + auto operator_0 = cudaq::scalar_operator(value_0); + auto operator_1 = cudaq::scalar_operator(value_1); + auto operator_2 = cudaq::scalar_operator(value_2); + auto operator_3 = cudaq::scalar_operator(value_3); + + auto got_value_0 = operator_0.evaluate({}); + auto got_value_1 = operator_1.evaluate({}); + auto got_value_2 = operator_2.evaluate({}); + auto got_value_3 = operator_3.evaluate({}); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } + + // From a lambda function. + { + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + std::map> parameter_map; + + auto operator_0 = cudaq::scalar_operator(function); + auto operator_1 = cudaq::scalar_operator(function); + auto operator_2 = cudaq::scalar_operator(function); + auto operator_3 = cudaq::scalar_operator(function); + + parameter_map["value"] = value_0; + auto got_value_0 = operator_0.evaluate(parameter_map); + parameter_map["value"] = value_1; + auto got_value_1 = operator_1.evaluate(parameter_map); + parameter_map["value"] = value_2; + auto got_value_2 = operator_2.evaluate(parameter_map); + parameter_map["value"] = value_3; + auto got_value_3 = operator_3.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } +} \ No newline at end of file From 18213610ba4ded6769b9ede64763416289f274c4 Mon Sep 17 00:00:00 2001 From: cuda-quantum-bot Date: Thu, 9 Jan 2025 17:37:24 +0000 Subject: [PATCH 119/311] Cleaning up docs preview for PR #12. From 3ecd247fa94971993ad06f97de62e54d46e0e915 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 9 Jan 2025 10:05:51 -0800 Subject: [PATCH 120/311] fixing spellings Signed-off-by: Sachin Pisal --- runtime/cudaq/operators.h | 18 +++++++++--------- runtime/cudaq/schedule.h | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 77ee4703bf..d668909daa 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -62,8 +62,8 @@ class operator_sum { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0:2, 1:2}`. - /// @arg `parameters` : A map of the paramter names to their concrete, complex - /// values. + /// @arg `parameters` : A map of the parameter names to their concrete, + /// complex values. matrix_2 to_matrix(const std::map &dimensions, const std::map ¶ms = {}) const; @@ -116,13 +116,13 @@ class operator_sum { /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms blockwise; it may hence + /// account, and does not try to reorder terms `blockwise`; it may hence /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. bool operator==(const operator_sum &other) const; - /// FIXME: Protect this once I can do deeper testing in unittests. + /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: std::vector get_terms() { return m_terms; } }; @@ -183,7 +183,7 @@ class product_operator : public operator_sum { /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms blockwise; it may hence + /// account, and does not try to reorder terms `blockwise`; it may hence /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. @@ -197,8 +197,8 @@ class product_operator : public operator_sum { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0:2, 1:2}`. - /// @arg `parameters` : A map of the paramter names to their concrete, complex - /// values. + /// @arg `parameters` : A map of the parameter names to their concrete, + /// complex values. matrix_2 to_matrix(std::map dimensions, std::map> parameters); @@ -214,7 +214,7 @@ class product_operator : public operator_sum { /// operator. int term_count() const { return m_terms.size(); } - /// FIXME: Protect this once I can do deeper testing in unittests. + /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: std::vector> get_terms() { return m_terms; @@ -419,7 +419,7 @@ class scalar_operator : public product_operator { std::vector _operators_to_compose; /// NOTE: We should revisit these constructors and remove any that have - /// become unecessary as the implementation improves. + /// become unnecessary as the implementation improves. scalar_operator() = default; // Copy constructor. scalar_operator(const scalar_operator &other); diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h index c9610cc75b..67d68fd951 100644 --- a/runtime/cudaq/schedule.h +++ b/runtime/cudaq/schedule.h @@ -1,5 +1,5 @@ /****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * * This source code and the accompanying materials are made available under * @@ -62,7 +62,7 @@ class Schedule { /// implementation phase. // Pointers. - /// @brief Dereference operator to access the current step value. + /// @brief `Dereference` operator to access the current step value. /// @return Reference to current complex step value. reference operator*() const; @@ -76,7 +76,7 @@ class Schedule { Schedule &operator++(); // Postfix increment. - /// @brief Postfix increment operator to move to the next step in the + /// @brief `Postfix` increment operator to move to the next step in the /// schedule. /// @return Copy of the previous Schedule state. Schedule operator++(int); From 33925b7a90390f8a4ac2c4647aaf4e52efe8d362 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 9 Jan 2025 10:21:04 -0800 Subject: [PATCH 121/311] Adding base_integrator and base_time_stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 58 +++++++++++++++++++++++++++++++ runtime/cudaq/base_time_stepper.h | 19 ++++++++++ 2 files changed, 77 insertions(+) create mode 100644 runtime/cudaq/base_integrator.h create mode 100644 runtime/cudaq/base_time_stepper.h diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h new file mode 100644 index 0000000000..a4dc982752 --- /dev/null +++ b/runtime/cudaq/base_integrator.h @@ -0,0 +1,58 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include "schedule.h" +#include "operators.h" + +namespace cudaq { +template +class BaseIntegrator { +protected: + std::map integrator_options; + TState state; + double t; + std::map dimensions; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; + std::shared_ptr> stepper; + std::vector> collapse_operators; + + virtual void post_init() = 0; + +public: + virtual ~BaseIntegrator() = default; + + void set_state(const TState &initial_state, double t0 = 0.0) { + state = initial_state; + t = t0; + } + + void set_system( + const std::map &dimensions, + std::shared_ptr schedule, + std::shared_ptr hamiltonian, + std::vector> collapse_operators = {} + ) { + this->dimensions = dimensions; + this->schedule = schedule; + this->hamiltonian = hamiltonian; + this->collapse_operators = collapse_operators; + } + + virtual void integrate(double t) = 0; + + std::pair get_state() const { + return {t, state}; + } +}; +} diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h new file mode 100644 index 0000000000..d92184825b --- /dev/null +++ b/runtime/cudaq/base_time_stepper.h @@ -0,0 +1,19 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +namespace cudaq { +template +class BaseTimeStepper { +public: + virtual ~BaseTimeStepper() = default; + + virtual void compute(TState &state, double t) = 0; +}; +} \ No newline at end of file From 8aab2158fe52c8ddf5df316d8ddc560c293f3e10 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 08:21:09 -0800 Subject: [PATCH 122/311] adding base_operators interface Signed-off-by: Sachin Pisal --- runtime/cudaq/base_operator.h | 36 +++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 runtime/cudaq/base_operator.h diff --git a/runtime/cudaq/base_operator.h b/runtime/cudaq/base_operator.h new file mode 100644 index 0000000000..fe604e067d --- /dev/null +++ b/runtime/cudaq/base_operator.h @@ -0,0 +1,36 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/matrix.h" +#include "cudaq/utils/tensor.h" +#include +#include +#include +#include + +namespace cudaq { +/// @brief Base class for all operator types. +class base_operator { +public: + virtual ~base_operator() = default; + + /// @brief Evaluate the operator with given parameters + virtual std::complex evaluate(const std::map> ¶meters) const = 0; + + /// @brief Convert the operator to a matrix representation. + virtual matrix_2 to_matrix(const std::map &dimensions, const std::map> ¶meters = {}) const = 0; + + /// @brief Convert the operator to a string representation. + virtual std::string to_string() const = 0; + + /// @brief Return the degrees of freedom that the operator acts on. + virtual std::vector get_degrees() const = 0; +}; +} From 39cf6abdee8e79d8d16c780c00e0d8bc3a88700a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 10:29:33 -0800 Subject: [PATCH 123/311] keeping degrees to match the current implementation Signed-off-by: Sachin Pisal --- runtime/cudaq/base_operator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/base_operator.h b/runtime/cudaq/base_operator.h index fe604e067d..ea3475a5ee 100644 --- a/runtime/cudaq/base_operator.h +++ b/runtime/cudaq/base_operator.h @@ -31,6 +31,6 @@ class base_operator { virtual std::string to_string() const = 0; /// @brief Return the degrees of freedom that the operator acts on. - virtual std::vector get_degrees() const = 0; + virtual std::vector degrees() const = 0; }; } From 599fb5731e81e5754897d43bb6efe4e7f19e845b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 10:37:39 -0800 Subject: [PATCH 124/311] adding base integrator and time stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 71 +++++++++++++++---------------- runtime/cudaq/base_time_stepper.h | 6 +-- 2 files changed, 37 insertions(+), 40 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index a4dc982752..15c7c2f7e0 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -8,51 +8,48 @@ #pragma once +#include "base_time_stepper.h" +#include "operators.h" +#include "schedule.h" #include -#include #include -#include "schedule.h" -#include "operators.h" +#include namespace cudaq { template class BaseIntegrator { protected: - std::map integrator_options; - TState state; - double t; - std::map dimensions; - std::shared_ptr schedule; - std::shared_ptr hamiltonian; - std::shared_ptr> stepper; - std::vector> collapse_operators; + std::map integrator_options; + TState state; + double t; + std::map dimensions; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; + std::shared_ptr> stepper; + std::vector> collapse_operators; - virtual void post_init() = 0; + virtual void post_init() = 0; public: - virtual ~BaseIntegrator() = default; - - void set_state(const TState &initial_state, double t0 = 0.0) { - state = initial_state; - t = t0; - } - - void set_system( - const std::map &dimensions, - std::shared_ptr schedule, - std::shared_ptr hamiltonian, - std::vector> collapse_operators = {} - ) { - this->dimensions = dimensions; - this->schedule = schedule; - this->hamiltonian = hamiltonian; - this->collapse_operators = collapse_operators; - } - - virtual void integrate(double t) = 0; - - std::pair get_state() const { - return {t, state}; - } + virtual ~BaseIntegrator() = default; + + void set_state(const TState &initial_state, double t0 = 0.0) { + state = initial_state; + t = t0; + } + + void set_system( + const std::map &dimensions, std::shared_ptr schedule, + std::shared_ptr hamiltonian, + std::vector> collapse_operators = {}) { + this->dimensions = dimensions; + this->schedule = schedule; + this->hamiltonian = hamiltonian; + this->collapse_operators = collapse_operators; + } + + virtual void integrate(double t) = 0; + + std::pair get_state() const { return {t, state}; } }; -} +} // namespace cudaq diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index d92184825b..ce1502269e 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -12,8 +12,8 @@ namespace cudaq { template class BaseTimeStepper { public: - virtual ~BaseTimeStepper() = default; + virtual ~BaseTimeStepper() = default; - virtual void compute(TState &state, double t) = 0; + virtual void compute(TState &state, double t) = 0; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file From c89e9d90580fe1f6976c3ee7884883aef3dd22fe Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 13 Jan 2025 20:33:12 -0800 Subject: [PATCH 125/311] * Adding base test cases for Runge-Kutta stepper and integrator * Removing base_operator class, as operator_sum will act as a base class Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 8 +- runtime/cudaq/base_operator.h | 36 ----- runtime/cudaq/base_time_stepper.h | 2 +- runtime/cudaq/runge_kutta_integrator.h | 46 ++++++ runtime/cudaq/runge_kutta_time_stepper.h | 33 ++++ unittests/CMakeLists.txt | 2 + unittests/dynamics/runge_kutta_test_helpers.h | 24 +++ .../dynamics/test_runge_kutta_integrator.cpp | 132 ++++++++++++++++ .../test_runge_kutta_time_stepper.cpp | 148 ++++++++++++++++++ 9 files changed, 390 insertions(+), 41 deletions(-) delete mode 100644 runtime/cudaq/base_operator.h create mode 100644 runtime/cudaq/runge_kutta_integrator.h create mode 100644 runtime/cudaq/runge_kutta_time_stepper.h create mode 100644 unittests/dynamics/runge_kutta_test_helpers.h create mode 100644 unittests/dynamics/test_runge_kutta_integrator.cpp create mode 100644 unittests/dynamics/test_runge_kutta_time_stepper.cpp diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 15c7c2f7e0..e3196bd52d 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -24,9 +24,9 @@ class BaseIntegrator { double t; std::map dimensions; std::shared_ptr schedule; - std::shared_ptr hamiltonian; + std::shared_ptr hamiltonian; std::shared_ptr> stepper; - std::vector> collapse_operators; + std::vector> collapse_operators; virtual void post_init() = 0; @@ -40,8 +40,8 @@ class BaseIntegrator { void set_system( const std::map &dimensions, std::shared_ptr schedule, - std::shared_ptr hamiltonian, - std::vector> collapse_operators = {}) { + std::shared_ptr hamiltonian, + std::vector> collapse_operators = {}) { this->dimensions = dimensions; this->schedule = schedule; this->hamiltonian = hamiltonian; diff --git a/runtime/cudaq/base_operator.h b/runtime/cudaq/base_operator.h deleted file mode 100644 index ea3475a5ee..0000000000 --- a/runtime/cudaq/base_operator.h +++ /dev/null @@ -1,36 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "cudaq/matrix.h" -#include "cudaq/utils/tensor.h" -#include -#include -#include -#include - -namespace cudaq { -/// @brief Base class for all operator types. -class base_operator { -public: - virtual ~base_operator() = default; - - /// @brief Evaluate the operator with given parameters - virtual std::complex evaluate(const std::map> ¶meters) const = 0; - - /// @brief Convert the operator to a matrix representation. - virtual matrix_2 to_matrix(const std::map &dimensions, const std::map> ¶meters = {}) const = 0; - - /// @brief Convert the operator to a string representation. - virtual std::string to_string() const = 0; - - /// @brief Return the degrees of freedom that the operator acts on. - virtual std::vector degrees() const = 0; -}; -} diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index ce1502269e..4488de8b44 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -14,6 +14,6 @@ class BaseTimeStepper { public: virtual ~BaseTimeStepper() = default; - virtual void compute(TState &state, double t) = 0; + virtual void compute(TState &state, double t, double step_size) = 0; }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h new file mode 100644 index 0000000000..41d3da1715 --- /dev/null +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -0,0 +1,46 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "base_integrator.h" +#include "runge_kutta_time_stepper.h" +#include + +namespace cudaq { +template +class RungeKuttaIntegrator : public BaseIntegrator { +public: + using DerivativeFunction = std::function; + + explicit RungeKuttaIntegrator(DerivativeFunction f) : stepper(std::make_shared>(f)) {} + + // Initializes the integrator + void post_init() override { + if (!this->stepper) { + throw std::runtime_error("Time stepper is not set"); + } + } + + // Advances the system's state from current time to `t` + void integrate(double target_t) override { + if (!this->schedule || !this->hamiltonian) { + throw std::runtime_error("System is not properly set!"); + } + + while (this->t < target_t) { + stepper->compute(this->state, this->t); + // Time step size + this->t += 0.01; + } + } + +private: + std::shared_ptr> stepper; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_time_stepper.h b/runtime/cudaq/runge_kutta_time_stepper.h new file mode 100644 index 0000000000..7718251a07 --- /dev/null +++ b/runtime/cudaq/runge_kutta_time_stepper.h @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "base_time_stepper.h" +#include + +namespace cudaq { +template +class RungeKuttaTimeStepper : public BaseTimeStepper { +public: + using DerivativeFunction = std::function; + + RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} + + void compute(TState &state, double t, double dt = 0.01) override { + // 4th order Runge-Kutta method + TState k1 = derivativeFunc(state, t); + TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); + TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); + TState k4 = derivativeFunc(state + dt * k3, t + dt); + + state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); + } + +private: + DerivativeFunction derivativeFunc; +}; +} \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index df4748f80d..ef98549244 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -49,6 +49,8 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/elementary_ops_simple.cpp dynamics/elementary_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp + dynamics/test_runge_kutta_time_stepper.cpp + dynamics/test_runge_kutta_integrator.cpp ) # Make it so we can get function symbols diff --git a/unittests/dynamics/runge_kutta_test_helpers.h b/unittests/dynamics/runge_kutta_test_helpers.h new file mode 100644 index 0000000000..28ec5c7723 --- /dev/null +++ b/unittests/dynamics/runge_kutta_test_helpers.h @@ -0,0 +1,24 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include + +// A simple state type +using TestState = double; + +// Simple derivative function: dx/dt = -x (exponential decay) +inline TestState simple_derivative(const TestState &state, double t) { + return -state; +} + +// A complex function: dx/dt = sin(t) +inline TestState sine_derivative(const TestState &state, double t) { + return std::sin(t); +} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp new file mode 100644 index 0000000000..431ea1457a --- /dev/null +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -0,0 +1,132 @@ +// /******************************************************************************* +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * +// ******************************************************************************/ + +#include "runge_kutta_test_helpers.h" +#include "cudaq/runge_kutta_integrator.h" +#include +#include +#include + +using namespace cudaq; + +// Test fixture class +class RungeKuttaIntegratorTest : public ::testing::Test { +protected: + RungeKuttaIntegrator *integrator; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; + + void SetUp() override { + integrator = new RungeKuttaIntegrator(simple_derivative); + // Initial state and time + integrator->set_state(1.0, 0.0); + + // A simple step sequence for the schedule + std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; + + // Dummy parameters + std::vector parameters = {"param1"}; + + // A simple parameter function + auto value_function = [](const std::string ¶m, const std::complex &step) { + return step; + }; + + // A valid schedule instance + schedule = std::make_shared(steps, parameters, value_function); + + // A simple hamiltonian as an operator_sum + hamiltonian = std::make_shared(); + *hamiltonian += 0.5 * elementary_operator::identity(0); + *hamiltonian += 0.5 * elementary_operator::number(0); + + // System with valid components + integrator->set_system({{0, 2}}, schedule, hamiltonian); + } + + void TearDown() override { + delete integrator; + } +}; + +// Basic integration +TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { + integrator->integrate(1.0); + + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Basic Runge-Kutta integration failed!"; +} + +// Time evolution +TEST_F(RungeKuttaIntegratorTest, TimeEvolution) { + integrator->integrate(2.0); + + double expected = 2.0; + + EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) << "Integrator did not correctly update time!"; +} + +// Large step size +TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { + integrator->integrate(5.0); + + double expected = std::exp(-5.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) << "Runge-Kutta integration failed for large step size!!"; +} + + +// // Integrating Sine function +// TEST_F(RungeKuttaIntegratorTest, SineFunction) { +// integrator = new RungeKuttaIntegrator(sine_derivative); +// integrator->set_state(1.0, 0.0); +// integrator->set_system({{0, 2}}, schedule, hamiltonian); + +// integrator->integrate(M_PI / 2); + +// double expected = std::cos(M_PI / 2); + +// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Runge-Kutta integration for sine function failed!"; +// } + + +// Small step size +TEST_F(RungeKuttaIntegratorTest, SmallStepIntegration) { + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); + + double step_size = 0.001; + while (integrator->get_state().first < 1.0) { + integrator->integrate(integrator->get_state().first + step_size); + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) << "Runge-Kutta integration for small step size failed!"; +} + + +// Large step size +TEST_F(RungeKuttaIntegratorTest, LargeStepIntegration) { + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); + + double step_size = 0.5; + double t = 0.0; + double target_t = 1.0; + while (t < target_t) { + integrator->integrate(std::min(t + step_size, target_t)); + t += step_size; + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) << "Runge-Kutta integration for large step size failed!"; +} diff --git a/unittests/dynamics/test_runge_kutta_time_stepper.cpp b/unittests/dynamics/test_runge_kutta_time_stepper.cpp new file mode 100644 index 0000000000..bc32bb587c --- /dev/null +++ b/unittests/dynamics/test_runge_kutta_time_stepper.cpp @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "runge_kutta_test_helpers.h" +#include "cudaq/runge_kutta_time_stepper.h" +#include +#include +#include + +// Test fixture class +class RungeKuttaTimeStepperTest : public ::testing::Test { +protected: + std::shared_ptr> stepper; + + void SetUp() override { + stepper = std::make_shared>(simple_derivative); + } +}; + +// Single step integration +TEST_F(RungeKuttaTimeStepperTest, SingleStep) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; + + stepper->compute(state, t, dt); + + // Expected result using analytical solution: x(t) = e^(-t) + double expected = std::exp(-dt); + + EXPECT_NEAR(state, expected, 1e-3) << "Single step Runge-Kutta integration failed!"; +} + +// Multiple step integration +TEST_F(RungeKuttaTimeStepperTest, MultipleSteps) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; + + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } + + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-2) << "Multiple step Runge-Kutta integration failed!"; +} + +// Convergence to Analytical Solution +TEST_F(RungeKuttaTimeStepperTest, Convergence) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.01; + int steps = 100; + + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta integration does not converge!"; +} + +// // Integrating Sine function +// TEST_F(RungeKuttaTimeStepperTest, SineFunction) { +// auto sine_stepper = std::make_shared>(sine_derivative); + +// // Initial values +// double state = 0.0; +// double t = 0.0; +// double dt = 0.1; +// int steps = 10; + +// for (int i = 0; i < steps; i++) { +// sine_stepper->compute(state, t, dt); +// } + +// // Expected integral of sin(t) over [0, 1] is 1 - cos(1) +// double expected = 1 - std::cos(1); + +// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine function failed!"; +// } + +// Handling small steps sizes +TEST_F(RungeKuttaTimeStepperTest, SmallStepSize) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1e-5; + int steps = 100000; + + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } + + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta fails with small step sizes!"; +} + +// Handling large steps sizes +TEST_F(RungeKuttaTimeStepperTest, LargeStepSize) { + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1.0; + + stepper->compute(state, t, dt); + + double expected = std::exp(-1.0); + + EXPECT_NEAR(state, expected, 1e-1) << "Runge-Kutta is unstable with large step sizes!"; +} + +// Constant derivative (dx/dt = 0) +TEST_F(RungeKuttaTimeStepperTest, ConstantFunction) { + auto constant_stepper = std::make_shared>( + [](const TestState &state, double t) { + return 0.0; + } + ); + + // Initial values + double state = 5.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; + + for (int i = 0; i < steps; i++) { + constant_stepper->compute(state, t, dt); + } + + EXPECT_NEAR(state, 5.0, 1e-6) << "Runge-Kutta should not change a constant function!"; +} + + + From 48d4b75e160d31ce02801eb8cbd5367fc1fcd40b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 15:12:20 -0800 Subject: [PATCH 126/311] * Adding helpers functionlity * Aggregating parameters * Extracting documentation * Extracting positional and keyword arguments * Generating all quantum states for given degrees and dimensions * Permuting a given matrix * Canonicalizing degrees * Adding test cases for the above functions * Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 4 +- runtime/cudaq/dynamics/helpers.cpp | 135 +++++++++++++ runtime/cudaq/helpers.h | 42 ++++ runtime/cudaq/runge_kutta_integrator.h | 41 ++-- runtime/cudaq/runge_kutta_time_stepper.h | 24 +-- unittests/CMakeLists.txt | 1 + unittests/dynamics/runge_kutta_test_helpers.h | 4 +- unittests/dynamics/test_helpers.cpp | 189 ++++++++++++++++++ .../dynamics/test_runge_kutta_integrator.cpp | 134 ++++++------- .../test_runge_kutta_time_stepper.cpp | 152 +++++++------- 10 files changed, 549 insertions(+), 177 deletions(-) create mode 100644 runtime/cudaq/dynamics/helpers.cpp create mode 100644 runtime/cudaq/helpers.h create mode 100644 unittests/dynamics/test_helpers.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 9709cd9a71..d608aba2c3 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -1,5 +1,5 @@ # ============================================================================ # -# Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # # All rights reserved. # # # # This source code and the accompanying materials are made available under # @@ -11,7 +11,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp ) add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp new file mode 100644 index 0000000000..ad86a2c26b --- /dev/null +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/helpers.h" +#include +#include + +namespace cudaq { +// Aggregate parameters from multiple mappings. +std::map OperatorHelpers::aggregate_parameters(const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; + } + } + } + + return parameter_descriptions; +} + +// Extract documentation for a specific parameter from docstring. +std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } + + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + R"(\s*(\(.*\))?:\s*(.*)$)", std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if(std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + } + } + } catch (...) { + return ""; + } + + return ""; +} + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> OperatorHelpers::args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); + } + } + + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); + } + } + + return {extracted_args, kwonly_dict}; +} + +// Generate all possible quantum states for given degrees and dimensions +std::vector OperatorHelpers::generate_all_states(const std::vector °rees, const std::map &dimensions) { + if (degrees.empty()) { + return {}; + } + + std::vector> states; + for (int state = 0; state < dimensions.at(degrees[0]); state++) { + states.push_back({std::to_string(state)}); + } + + for (size_t i = 1; i < degrees.size(); i++) { + std::vector> new_states; + for (const auto ¤t : states) { + for (int state = 0; state < dimensions.at(degrees[i]); state++) { + auto new_entry = current; + new_entry.push_back(std::to_string(state)); + new_states.push_back(new_entry); + } + } + states = new_states; + } + + std::vector result; + for (const auto &state : states) { + std::ostringstream joined; + for (const auto &s : state) { + joined << s; + } + result.push_back(joined.str()); + } + return result; +} + +// Permute a given eigen matrix +void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation) { + Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); + + for (size_t i = 0; i < permutation.size(); i++) { + for (size_t j = 0; j < permutation.size(); j++) { + permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); + } + } + + matrix = permuted_matrix; +} + +// Canonicalize degrees by sorting in descending order +std::vector OperatorHelpers::canonicalize_degrees(const std::vector °rees) { + std::vector sorted_degrees = degrees; + std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); + return sorted_degrees; +} +} \ No newline at end of file diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h new file mode 100644 index 0000000000..488ff3bf0c --- /dev/null +++ b/runtime/cudaq/helpers.h @@ -0,0 +1,42 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cudaq { +class OperatorHelpers { +public: + // Aggregate parameters from multiple mappings. + static std::map aggregate_parameters(const std::vector> ¶meter_mappings); + + // Extract documentation for a specific parameter from docstring. + static std::string parameter_docs(const std::string ¶m_name, const std::string &docs); + + // Extract positional arguments and keyword-only arguments. + static std::pair, std::map> args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, const std::vector &kwonly_args); + + // Generate all possible quantum states for given degrees and dimensions. + static std::vector generate_all_states(const std::vector °rees, const std::map &dimensions); + + // Permute a given Eigen matrix. + static void permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation); + + // Canonicalize degrees by sorting in descending order. + static std::vector canonicalize_degrees(const std::vector °rees); +}; +} \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 41d3da1715..9914258386 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -16,31 +16,32 @@ namespace cudaq { template class RungeKuttaIntegrator : public BaseIntegrator { public: - using DerivativeFunction = std::function; + using DerivativeFunction = std::function; - explicit RungeKuttaIntegrator(DerivativeFunction f) : stepper(std::make_shared>(f)) {} + explicit RungeKuttaIntegrator(DerivativeFunction f) + : stepper(std::make_shared>(f)) {} - // Initializes the integrator - void post_init() override { - if (!this->stepper) { - throw std::runtime_error("Time stepper is not set"); - } + // Initializes the integrator + void post_init() override { + if (!this->stepper) { + throw std::runtime_error("Time stepper is not set"); } + } - // Advances the system's state from current time to `t` - void integrate(double target_t) override { - if (!this->schedule || !this->hamiltonian) { - throw std::runtime_error("System is not properly set!"); - } - - while (this->t < target_t) { - stepper->compute(this->state, this->t); - // Time step size - this->t += 0.01; - } + // Advances the system's state from current time to `t` + void integrate(double target_t) override { + if (!this->schedule || !this->hamiltonian) { + throw std::runtime_error("System is not properly set!"); } + while (this->t < target_t) { + stepper->compute(this->state, this->t); + // Time step size + this->t += 0.01; + } + } + private: - std::shared_ptr> stepper; + std::shared_ptr> stepper; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/runge_kutta_time_stepper.h b/runtime/cudaq/runge_kutta_time_stepper.h index 7718251a07..1dcd1f69cc 100644 --- a/runtime/cudaq/runge_kutta_time_stepper.h +++ b/runtime/cudaq/runge_kutta_time_stepper.h @@ -13,21 +13,21 @@ namespace cudaq { template class RungeKuttaTimeStepper : public BaseTimeStepper { public: - using DerivativeFunction = std::function; + using DerivativeFunction = std::function; - RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} + RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} - void compute(TState &state, double t, double dt = 0.01) override { - // 4th order Runge-Kutta method - TState k1 = derivativeFunc(state, t); - TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); - TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); - TState k4 = derivativeFunc(state + dt * k3, t + dt); + void compute(TState &state, double t, double dt = 0.01) override { + // 4th order Runge-Kutta method + TState k1 = derivativeFunc(state, t); + TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); + TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); + TState k4 = derivativeFunc(state + dt * k3, t + dt); - state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); - } + state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); + } private: - DerivativeFunction derivativeFunc; + DerivativeFunction derivativeFunc; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ef98549244..f788ea0c48 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -51,6 +51,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/product_operators_arithmetic.cpp dynamics/test_runge_kutta_time_stepper.cpp dynamics/test_runge_kutta_integrator.cpp + dynamics/test_helpers.cpp ) # Make it so we can get function symbols diff --git a/unittests/dynamics/runge_kutta_test_helpers.h b/unittests/dynamics/runge_kutta_test_helpers.h index 28ec5c7723..4f93ffa242 100644 --- a/unittests/dynamics/runge_kutta_test_helpers.h +++ b/unittests/dynamics/runge_kutta_test_helpers.h @@ -15,10 +15,10 @@ using TestState = double; // Simple derivative function: dx/dt = -x (exponential decay) inline TestState simple_derivative(const TestState &state, double t) { - return -state; + return -state; } // A complex function: dx/dt = sin(t) inline TestState sine_derivative(const TestState &state, double t) { - return std::sin(t); + return std::sin(t); } diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp new file mode 100644 index 0000000000..9e471a29bd --- /dev/null +++ b/unittests/dynamics/test_helpers.cpp @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/helpers.h" +#include +#include + +using namespace cudaq; + +TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { + std::vector> mappings = { + {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}} + }; + + auto result = OperatorHelpers::aggregate_parameters(mappings); + + EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); + EXPECT_EQ(result["beta"], "Parameter B"); + EXPECT_EQ(result["gamma"], "New Parameter"); +} + +TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { + std::vector> mappings; + auto result = OperatorHelpers::aggregate_parameters(mappings); + + EXPECT_TRUE(result.empty()); +} + +TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { + std::string docstring = + "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; + + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, "The first parameter."); + + result = OperatorHelpers::parameter_docs("beta", docstring); + EXPECT_EQ(result, "The second parameter."); +} + +TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { + std::string docstring = + "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; + + auto result = OperatorHelpers::parameter_docs("gamma", docstring); + EXPECT_EQ(result, ""); +} + +TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { + std::string docstring = ""; + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, ""); +} + +TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { + std::vector degrees = {0, 1}; + std::map dimensions = {{0, 2}, {1, 2}}; + + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"00", "01", "10", "11"}; + + EXPECT_EQ(states, expected_states); +} + +TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"000", "001", "010", "011", "100", "101", "110", "111"}; + + EXPECT_EQ(states, expected_states); +} + +TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { + std::vector degrees; + std::map dimensions; + + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + EXPECT_TRUE(states.empty()); +} + +TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}}; + + EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), std::out_of_range); +} + +TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { + Eigen::MatrixXcd matrix(2, 2); + matrix << 1, 2, + 3, 4; + + // Swap rows and columns + std::vector permutation = {1, 0}; + + OperatorHelpers::permute_matrix(matrix, permutation); + + Eigen::MatrixXcd expected(2, 2); + expected << 4, 3, + 2, 1; + + EXPECT_EQ(matrix, expected); +} + +TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { + Eigen::MatrixXcd matrix(3, 3); + matrix << 1, 2, 3, + 4, 5, 6, + 7, 8, 9; + + // No change + std::vector permutation = {0, 1, 2}; + + OperatorHelpers::permute_matrix(matrix, permutation); + + Eigen::MatrixXcd expected(3, 3); + expected << 1, 2, 3, + 4, 5, 6, + 7, 8, 9; + + EXPECT_EQ(matrix, expected); +} + +TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { + std::vector degrees = {3, 1, 2}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{3, 2, 1})); +} + +TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { + std::vector degrees = {5, 4, 3, 2, 1}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); +} + +TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { + std::vector degrees; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_TRUE(sorted.empty()); +} + +TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { + std::map kwargs = { + {"alpha", "0.5"}, + {"beta", "1.0"}, + {"gamma", "2.0"} + }; + + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; + + auto [args, kwonly] = OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); + + EXPECT_EQ(args.size(), 2); + EXPECT_EQ(args[0], "0.5"); + EXPECT_EQ(args[1], "1.0"); + + EXPECT_EQ(kwonly.size(), 1); + EXPECT_EQ(kwonly["gamma"], "2.0"); +} + + +TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { + std::map kwargs = { + {"beta", "1.0"}, + {"gamma", "2.0"} + }; + + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; + + EXPECT_THROW(OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), std::invalid_argument); +} + + + + diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 431ea1457a..c75a7c8d6d 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -1,88 +1,87 @@ // /******************************************************************************* -// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * -// * All rights reserved. * -// * * -// * This source code and the accompanying materials are made available under * -// * the terms of the Apache License 2.0 which accompanies this distribution. * +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * // ******************************************************************************/ -#include "runge_kutta_test_helpers.h" #include "cudaq/runge_kutta_integrator.h" +#include "runge_kutta_test_helpers.h" +#include #include #include -#include using namespace cudaq; // Test fixture class class RungeKuttaIntegratorTest : public ::testing::Test { protected: - RungeKuttaIntegrator *integrator; - std::shared_ptr schedule; - std::shared_ptr hamiltonian; + RungeKuttaIntegrator *integrator; + std::shared_ptr schedule; + std::shared_ptr hamiltonian; - void SetUp() override { - integrator = new RungeKuttaIntegrator(simple_derivative); - // Initial state and time - integrator->set_state(1.0, 0.0); + void SetUp() override { + integrator = new RungeKuttaIntegrator(simple_derivative); + // Initial state and time + integrator->set_state(1.0, 0.0); - // A simple step sequence for the schedule - std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; + // A simple step sequence for the schedule + std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; - // Dummy parameters - std::vector parameters = {"param1"}; + // Dummy parameters + std::vector parameters = {"param1"}; - // A simple parameter function - auto value_function = [](const std::string ¶m, const std::complex &step) { - return step; - }; + // A simple parameter function + auto value_function = [](const std::string ¶m, + const std::complex &step) { return step; }; - // A valid schedule instance - schedule = std::make_shared(steps, parameters, value_function); + // A valid schedule instance + schedule = std::make_shared(steps, parameters, value_function); - // A simple hamiltonian as an operator_sum - hamiltonian = std::make_shared(); - *hamiltonian += 0.5 * elementary_operator::identity(0); - *hamiltonian += 0.5 * elementary_operator::number(0); + // A simple hamiltonian as an operator_sum + hamiltonian = std::make_shared(); + *hamiltonian += 0.5 * elementary_operator::identity(0); + *hamiltonian += 0.5 * elementary_operator::number(0); - // System with valid components - integrator->set_system({{0, 2}}, schedule, hamiltonian); - } + // System with valid components + integrator->set_system({{0, 2}}, schedule, hamiltonian); + } - void TearDown() override { - delete integrator; - } + void TearDown() override { delete integrator; } }; // Basic integration TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { - integrator->integrate(1.0); + integrator->integrate(1.0); - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); - EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Basic Runge-Kutta integration failed!"; + EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) + << "Basic Runge-Kutta integration failed!"; } // Time evolution TEST_F(RungeKuttaIntegratorTest, TimeEvolution) { - integrator->integrate(2.0); + integrator->integrate(2.0); - double expected = 2.0; + double expected = 2.0; - EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) << "Integrator did not correctly update time!"; + EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) + << "Integrator did not correctly update time!"; } // Large step size TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { - integrator->integrate(5.0); + integrator->integrate(5.0); - double expected = std::exp(-5.0); + double expected = std::exp(-5.0); - EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) << "Runge-Kutta integration failed for large step size!!"; + EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) + << "Runge-Kutta integration failed for large step size!!"; } - // // Integrating Sine function // TEST_F(RungeKuttaIntegratorTest, SineFunction) { // integrator = new RungeKuttaIntegrator(sine_derivative); @@ -93,40 +92,41 @@ TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { // double expected = std::cos(M_PI / 2); -// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << "Runge-Kutta integration for sine function failed!"; +// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << +// "Runge-Kutta integration for sine function failed!"; // } - // Small step size TEST_F(RungeKuttaIntegratorTest, SmallStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); - double step_size = 0.001; - while (integrator->get_state().first < 1.0) { - integrator->integrate(integrator->get_state().first + step_size); - } + double step_size = 0.001; + while (integrator->get_state().first < 1.0) { + integrator->integrate(integrator->get_state().first + step_size); + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) << "Runge-Kutta integration for small step size failed!"; + EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) + << "Runge-Kutta integration for small step size failed!"; } - // Large step size TEST_F(RungeKuttaIntegratorTest, LargeStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); + integrator->set_state(1.0, 0.0); + integrator->set_system({{0, 2}}, schedule, hamiltonian); - double step_size = 0.5; - double t = 0.0; - double target_t = 1.0; - while (t < target_t) { - integrator->integrate(std::min(t + step_size, target_t)); - t += step_size; - } + double step_size = 0.5; + double t = 0.0; + double target_t = 1.0; + while (t < target_t) { + integrator->integrate(std::min(t + step_size, target_t)); + t += step_size; + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) << "Runge-Kutta integration for large step size failed!"; + EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) + << "Runge-Kutta integration for large step size failed!"; } diff --git a/unittests/dynamics/test_runge_kutta_time_stepper.cpp b/unittests/dynamics/test_runge_kutta_time_stepper.cpp index bc32bb587c..4c4c7b7588 100644 --- a/unittests/dynamics/test_runge_kutta_time_stepper.cpp +++ b/unittests/dynamics/test_runge_kutta_time_stepper.cpp @@ -6,75 +6,80 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "runge_kutta_test_helpers.h" #include "cudaq/runge_kutta_time_stepper.h" +#include "runge_kutta_test_helpers.h" +#include #include #include -#include // Test fixture class class RungeKuttaTimeStepperTest : public ::testing::Test { protected: - std::shared_ptr> stepper; + std::shared_ptr> stepper; - void SetUp() override { - stepper = std::make_shared>(simple_derivative); - } + void SetUp() override { + stepper = std::make_shared>( + simple_derivative); + } }; // Single step integration TEST_F(RungeKuttaTimeStepperTest, SingleStep) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; - stepper->compute(state, t, dt); + stepper->compute(state, t, dt); - // Expected result using analytical solution: x(t) = e^(-t) - double expected = std::exp(-dt); + // Expected result using analytical solution: x(t) = e^(-t) + double expected = std::exp(-dt); - EXPECT_NEAR(state, expected, 1e-3) << "Single step Runge-Kutta integration failed!"; + EXPECT_NEAR(state, expected, 1e-3) + << "Single step Runge-Kutta integration failed!"; } // Multiple step integration TEST_F(RungeKuttaTimeStepperTest, MultipleSteps) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); + // Expected result: x(t) = e^(-t) + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-2) << "Multiple step Runge-Kutta integration failed!"; + EXPECT_NEAR(state, expected, 1e-2) + << "Multiple step Runge-Kutta integration failed!"; } // Convergence to Analytical Solution TEST_F(RungeKuttaTimeStepperTest, Convergence) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.01; - int steps = 100; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 0.01; + int steps = 100; - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta integration does not converge!"; + EXPECT_NEAR(state, expected, 1e-3) + << "Runge-Kutta integration does not converge!"; } // // Integrating Sine function // TEST_F(RungeKuttaTimeStepperTest, SineFunction) { -// auto sine_stepper = std::make_shared>(sine_derivative); +// auto sine_stepper = +// std::make_shared>(sine_derivative); // // Initial values // double state = 0.0; @@ -89,60 +94,59 @@ TEST_F(RungeKuttaTimeStepperTest, Convergence) { // // Expected integral of sin(t) over [0, 1] is 1 - cos(1) // double expected = 1 - std::cos(1); -// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine function failed!"; +// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine +// function failed!"; // } // Handling small steps sizes TEST_F(RungeKuttaTimeStepperTest, SmallStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1e-5; - int steps = 100000; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1e-5; + int steps = 100000; - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } + for (int i = 0; i < steps; i++) { + stepper->compute(state, t, dt); + } - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-3) << "Runge-Kutta fails with small step sizes!"; + EXPECT_NEAR(state, expected, 1e-3) + << "Runge-Kutta fails with small step sizes!"; } // Handling large steps sizes TEST_F(RungeKuttaTimeStepperTest, LargeStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1.0; + // Initial values + double state = 1.0; + double t = 0.0; + double dt = 1.0; - stepper->compute(state, t, dt); + stepper->compute(state, t, dt); - double expected = std::exp(-1.0); + double expected = std::exp(-1.0); - EXPECT_NEAR(state, expected, 1e-1) << "Runge-Kutta is unstable with large step sizes!"; + EXPECT_NEAR(state, expected, 1e-1) + << "Runge-Kutta is unstable with large step sizes!"; } // Constant derivative (dx/dt = 0) TEST_F(RungeKuttaTimeStepperTest, ConstantFunction) { - auto constant_stepper = std::make_shared>( - [](const TestState &state, double t) { - return 0.0; - } - ); - - // Initial values - double state = 5.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; - - for (int i = 0; i < steps; i++) { - constant_stepper->compute(state, t, dt); - } - - EXPECT_NEAR(state, 5.0, 1e-6) << "Runge-Kutta should not change a constant function!"; + auto constant_stepper = + std::make_shared>( + [](const TestState &state, double t) { return 0.0; }); + + // Initial values + double state = 5.0; + double t = 0.0; + double dt = 0.1; + int steps = 10; + + for (int i = 0; i < steps; i++) { + constant_stepper->compute(state, t, dt); + } + + EXPECT_NEAR(state, 5.0, 1e-6) + << "Runge-Kutta should not change a constant function!"; } - - - From 4e9525804f246e6042eeb159d8c8b030888d8953 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 15:18:55 -0800 Subject: [PATCH 127/311] Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 198 +++++++++++++++------------- runtime/cudaq/helpers.h | 56 ++++---- unittests/dynamics/test_helpers.cpp | 193 +++++++++++++-------------- 3 files changed, 227 insertions(+), 220 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index ad86a2c26b..e6c50d14a0 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,129 +7,141 @@ ******************************************************************************/ #include "cudaq/helpers.h" -#include #include +#include namespace cudaq { // Aggregate parameters from multiple mappings. -std::map OperatorHelpers::aggregate_parameters(const std::vector> ¶meter_mappings) { - std::map parameter_descriptions; - - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; - } - } +std::map OperatorHelpers::aggregate_parameters( + const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; + } } + } - return parameter_descriptions; + return parameter_descriptions; } // Extract documentation for a specific parameter from docstring. -std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; - } - - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + R"(\s*(\(.*\))?:\s*(.*)$)", std::regex::multiline); - - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; - - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if(std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); - } - } - } catch (...) { - return ""; +std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, + const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } + + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", + std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if (std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + } } - + } catch (...) { return ""; + } + + return ""; } // Extract positional arguments and keyword-only arguments. -std::pair, std::map> OperatorHelpers::args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); - } +std::pair, std::map> +OperatorHelpers::args_from_kwargs( + const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); } + } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); - } + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); } + } - return {extracted_args, kwonly_dict}; + return {extracted_args, kwonly_dict}; } // Generate all possible quantum states for given degrees and dimensions -std::vector OperatorHelpers::generate_all_states(const std::vector °rees, const std::map &dimensions) { - if (degrees.empty()) { - return {}; - } - - std::vector> states; - for (int state = 0; state < dimensions.at(degrees[0]); state++) { - states.push_back({std::to_string(state)}); +std::vector +OperatorHelpers::generate_all_states(const std::vector °rees, + const std::map &dimensions) { + if (degrees.empty()) { + return {}; + } + + std::vector> states; + for (int state = 0; state < dimensions.at(degrees[0]); state++) { + states.push_back({std::to_string(state)}); + } + + for (size_t i = 1; i < degrees.size(); i++) { + std::vector> new_states; + for (const auto ¤t : states) { + for (int state = 0; state < dimensions.at(degrees[i]); state++) { + auto new_entry = current; + new_entry.push_back(std::to_string(state)); + new_states.push_back(new_entry); + } } - - for (size_t i = 1; i < degrees.size(); i++) { - std::vector> new_states; - for (const auto ¤t : states) { - for (int state = 0; state < dimensions.at(degrees[i]); state++) { - auto new_entry = current; - new_entry.push_back(std::to_string(state)); - new_states.push_back(new_entry); - } - } - states = new_states; - } - - std::vector result; - for (const auto &state : states) { - std::ostringstream joined; - for (const auto &s : state) { - joined << s; - } - result.push_back(joined.str()); + states = new_states; + } + + std::vector result; + for (const auto &state : states) { + std::ostringstream joined; + for (const auto &s : state) { + joined << s; } - return result; + result.push_back(joined.str()); + } + return result; } // Permute a given eigen matrix -void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation) { - Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); +void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, + const std::vector &permutation) { + Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); - for (size_t i = 0; i < permutation.size(); i++) { - for (size_t j = 0; j < permutation.size(); j++) { - permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); - } + for (size_t i = 0; i < permutation.size(); i++) { + for (size_t j = 0; j < permutation.size(); j++) { + permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); } + } - matrix = permuted_matrix; + matrix = permuted_matrix; } // Canonicalize degrees by sorting in descending order -std::vector OperatorHelpers::canonicalize_degrees(const std::vector °rees) { - std::vector sorted_degrees = degrees; - std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); - return sorted_degrees; +std::vector +OperatorHelpers::canonicalize_degrees(const std::vector °rees) { + std::vector sorted_degrees = degrees; + std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); + return sorted_degrees; } -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 488ff3bf0c..52bb131d0c 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -8,35 +8,43 @@ #pragma once -#include -#include -#include +#include #include #include -#include +#include #include -#include +#include +#include +#include namespace cudaq { class OperatorHelpers { public: - // Aggregate parameters from multiple mappings. - static std::map aggregate_parameters(const std::vector> ¶meter_mappings); - - // Extract documentation for a specific parameter from docstring. - static std::string parameter_docs(const std::string ¶m_name, const std::string &docs); - - // Extract positional arguments and keyword-only arguments. - static std::pair, std::map> args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, const std::vector &kwonly_args); - - // Generate all possible quantum states for given degrees and dimensions. - static std::vector generate_all_states(const std::vector °rees, const std::map &dimensions); - - // Permute a given Eigen matrix. - static void permute_matrix(Eigen::MatrixXcd &matrix, const std::vector &permutation); - - // Canonicalize degrees by sorting in descending order. - static std::vector canonicalize_degrees(const std::vector °rees); + // Aggregate parameters from multiple mappings. + static std::map + aggregate_parameters(const std::vector> + ¶meter_mappings); + + // Extract documentation for a specific parameter from docstring. + static std::string parameter_docs(const std::string ¶m_name, + const std::string &docs); + + // Extract positional arguments and keyword-only arguments. + static std::pair, std::map> + args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args); + + // Generate all possible quantum states for given degrees and dimensions. + static std::vector + generate_all_states(const std::vector °rees, + const std::map &dimensions); + + // Permute a given Eigen matrix. + static void permute_matrix(Eigen::MatrixXcd &matrix, + const std::vector &permutation); + + // Canonicalize degrees by sorting in descending order. + static std::vector canonicalize_degrees(const std::vector °rees); }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp index 9e471a29bd..aa14cb5c10 100644 --- a/unittests/dynamics/test_helpers.cpp +++ b/unittests/dynamics/test_helpers.cpp @@ -13,177 +13,164 @@ using namespace cudaq; TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { - std::vector> mappings = { - {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}} - }; + std::vector> mappings = { + {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, + {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}}}; - auto result = OperatorHelpers::aggregate_parameters(mappings); + auto result = OperatorHelpers::aggregate_parameters(mappings); - EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); - EXPECT_EQ(result["beta"], "Parameter B"); - EXPECT_EQ(result["gamma"], "New Parameter"); + EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); + EXPECT_EQ(result["beta"], "Parameter B"); + EXPECT_EQ(result["gamma"], "New Parameter"); } TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { - std::vector> mappings; - auto result = OperatorHelpers::aggregate_parameters(mappings); + std::vector> mappings; + auto result = OperatorHelpers::aggregate_parameters(mappings); - EXPECT_TRUE(result.empty()); + EXPECT_TRUE(result.empty()); } TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { - std::string docstring = - "Description of function.\n" - "Arguments:\n" - " alpha (float): The first parameter.\n" - " beta (int): The second parameter."; + std::string docstring = "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); - EXPECT_EQ(result, "The first parameter."); + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, "The first parameter."); - result = OperatorHelpers::parameter_docs("beta", docstring); - EXPECT_EQ(result, "The second parameter."); + result = OperatorHelpers::parameter_docs("beta", docstring); + EXPECT_EQ(result, "The second parameter."); } TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { - std::string docstring = - "Description of function.\n" - "Arguments:\n" - " alpha (float): The first parameter.\n" - " beta (int): The second parameter."; + std::string docstring = "Description of function.\n" + "Arguments:\n" + " alpha (float): The first parameter.\n" + " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("gamma", docstring); - EXPECT_EQ(result, ""); + auto result = OperatorHelpers::parameter_docs("gamma", docstring); + EXPECT_EQ(result, ""); } TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { - std::string docstring = ""; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); - EXPECT_EQ(result, ""); + std::string docstring = ""; + auto result = OperatorHelpers::parameter_docs("alpha", docstring); + EXPECT_EQ(result, ""); } TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { - std::vector degrees = {0, 1}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::vector degrees = {0, 1}; + std::map dimensions = {{0, 2}, {1, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); - std::vector expected_states = {"00", "01", "10", "11"}; + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"00", "01", "10", "11"}; - EXPECT_EQ(states, expected_states); + EXPECT_EQ(states, expected_states); } TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { - std::vector degrees = {0, 1, 2}; - std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); - std::vector expected_states = {"000", "001", "010", "011", "100", "101", "110", "111"}; + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + std::vector expected_states = {"000", "001", "010", "011", + "100", "101", "110", "111"}; - EXPECT_EQ(states, expected_states); + EXPECT_EQ(states, expected_states); } TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { - std::vector degrees; - std::map dimensions; + std::vector degrees; + std::map dimensions; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); - EXPECT_TRUE(states.empty()); + auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + EXPECT_TRUE(states.empty()); } TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { - std::vector degrees = {0, 1, 2}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::vector degrees = {0, 1, 2}; + std::map dimensions = {{0, 2}, {1, 2}}; - EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), std::out_of_range); + EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), + std::out_of_range); } TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { - Eigen::MatrixXcd matrix(2, 2); - matrix << 1, 2, - 3, 4; + Eigen::MatrixXcd matrix(2, 2); + matrix << 1, 2, 3, 4; - // Swap rows and columns - std::vector permutation = {1, 0}; + // Swap rows and columns + std::vector permutation = {1, 0}; - OperatorHelpers::permute_matrix(matrix, permutation); + OperatorHelpers::permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(2, 2); - expected << 4, 3, - 2, 1; + Eigen::MatrixXcd expected(2, 2); + expected << 4, 3, 2, 1; - EXPECT_EQ(matrix, expected); + EXPECT_EQ(matrix, expected); } TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { - Eigen::MatrixXcd matrix(3, 3); - matrix << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; + Eigen::MatrixXcd matrix(3, 3); + matrix << 1, 2, 3, 4, 5, 6, 7, 8, 9; - // No change - std::vector permutation = {0, 1, 2}; + // No change + std::vector permutation = {0, 1, 2}; - OperatorHelpers::permute_matrix(matrix, permutation); + OperatorHelpers::permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(3, 3); - expected << 1, 2, 3, - 4, 5, 6, - 7, 8, 9; + Eigen::MatrixXcd expected(3, 3); + expected << 1, 2, 3, 4, 5, 6, 7, 8, 9; - EXPECT_EQ(matrix, expected); + EXPECT_EQ(matrix, expected); } TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { - std::vector degrees = {3, 1, 2}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); - EXPECT_EQ(sorted, (std::vector{3, 2, 1})); + std::vector degrees = {3, 1, 2}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { - std::vector degrees = {5, 4, 3, 2, 1}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); - EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); + std::vector degrees = {5, 4, 3, 2, 1}; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { - std::vector degrees; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); - EXPECT_TRUE(sorted.empty()); + std::vector degrees; + auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + EXPECT_TRUE(sorted.empty()); } TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { - std::map kwargs = { - {"alpha", "0.5"}, - {"beta", "1.0"}, - {"gamma", "2.0"} - }; + std::map kwargs = { + {"alpha", "0.5"}, {"beta", "1.0"}, {"gamma", "2.0"}}; - std::vector required_args = {"alpha", "beta"}; - std::vector kwonly_args = {"gamma"}; + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; - auto [args, kwonly] = OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); + auto [args, kwonly] = + OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); - EXPECT_EQ(args.size(), 2); - EXPECT_EQ(args[0], "0.5"); - EXPECT_EQ(args[1], "1.0"); - - EXPECT_EQ(kwonly.size(), 1); - EXPECT_EQ(kwonly["gamma"], "2.0"); -} + EXPECT_EQ(args.size(), 2); + EXPECT_EQ(args[0], "0.5"); + EXPECT_EQ(args[1], "1.0"); + EXPECT_EQ(kwonly.size(), 1); + EXPECT_EQ(kwonly["gamma"], "2.0"); +} TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { - std::map kwargs = { - {"beta", "1.0"}, - {"gamma", "2.0"} - }; + std::map kwargs = {{"beta", "1.0"}, + {"gamma", "2.0"}}; - std::vector required_args = {"alpha", "beta"}; - std::vector kwonly_args = {"gamma"}; + std::vector required_args = {"alpha", "beta"}; + std::vector kwonly_args = {"gamma"}; - EXPECT_THROW(OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), std::invalid_argument); + EXPECT_THROW( + OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), + std::invalid_argument); } - - - - From fb1ad13f451e7a95bcc8f41e2b3c7638fcfa0442 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 17:56:42 -0800 Subject: [PATCH 128/311] Adding evolution API header Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 runtime/cudaq/evolution.h diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h new file mode 100644 index 0000000000..3b86c448ce --- /dev/null +++ b/runtime/cudaq/evolution.h @@ -0,0 +1,46 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/operators.h" +#include "cudaq/schedule.h" +#include "cudaq/base_integrator.h" +#include "common/EvolveResult.h" + +#include +#include +#include +#include +#include + +namespace cudaq { +class Evolution { +public: + /// Computes the Taylor series expansion of the matrix exponential. + static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, int order = 20); + + /// Computes the evolution step matrix + static Eigen::MatrixXcd compute_step_matrix(const operator_sum &hamiltonian, const std::map &dimensions, const std::map> ¶meters, double dt, bool use_gpu = false); + + /// Adds noise channels based on collapse operators. + static void add_noise_channel_for_step(const std::string &step_kernel_name, cudaq::noise_model &noise_model, const std::vector &collapse_operators, const std::map &dimensions, const std::map> ¶meters, double dt); + + /// Launches an analog Hamiltonian kernel for quantum simulations. + static evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, const operator_sum &hamiltonian, const std::shared_ptr &schedule, int shots_count, bool is_async = false); + + /// Generates evolution kernels for the simulation. + static std::vector evolution_kernel(int num_qubits, const std::function> &, double)> &compute_step_matrix, const std::vector tlist, const std::vector>> &schedule_parameters); + + /// Evolves a single quantum state under a given hamiltonian. + static evolve_result evolve_single(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, state initial_state, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); + + /// Evolves a single or multiple quantum states under a given hamiltonian. + static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, const std::vector &initial_states, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); +}; +} \ No newline at end of file From f07c55b2516799fc35ebe5aaee673f137e6a705c Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 14 Jan 2025 17:57:19 -0800 Subject: [PATCH 129/311] Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 69 +++++++++++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 17 deletions(-) diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 3b86c448ce..c2915319e2 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -8,10 +8,10 @@ #pragma once +#include "common/EvolveResult.h" +#include "cudaq/base_integrator.h" #include "cudaq/operators.h" #include "cudaq/schedule.h" -#include "cudaq/base_integrator.h" -#include "common/EvolveResult.h" #include #include @@ -22,25 +22,60 @@ namespace cudaq { class Evolution { public: - /// Computes the Taylor series expansion of the matrix exponential. - static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, int order = 20); + /// Computes the Taylor series expansion of the matrix exponential. + static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, + int order = 20); - /// Computes the evolution step matrix - static Eigen::MatrixXcd compute_step_matrix(const operator_sum &hamiltonian, const std::map &dimensions, const std::map> ¶meters, double dt, bool use_gpu = false); + /// Computes the evolution step matrix + static Eigen::MatrixXcd compute_step_matrix( + const operator_sum &hamiltonian, const std::map &dimensions, + const std::map> ¶meters, double dt, + bool use_gpu = false); - /// Adds noise channels based on collapse operators. - static void add_noise_channel_for_step(const std::string &step_kernel_name, cudaq::noise_model &noise_model, const std::vector &collapse_operators, const std::map &dimensions, const std::map> ¶meters, double dt); + /// Adds noise channels based on collapse operators. + static void add_noise_channel_for_step( + const std::string &step_kernel_name, cudaq::noise_model &noise_model, + const std::vector &collapse_operators, + const std::map &dimensions, + const std::map> ¶meters, double dt); - /// Launches an analog Hamiltonian kernel for quantum simulations. - static evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, const operator_sum &hamiltonian, const std::shared_ptr &schedule, int shots_count, bool is_async = false); + /// Launches an analog Hamiltonian kernel for quantum simulations. + static evolve_result + launch_analog_hamiltonian_kernel(const std::string &target_name, + const operator_sum &hamiltonian, + const std::shared_ptr &schedule, + int shots_count, bool is_async = false); - /// Generates evolution kernels for the simulation. - static std::vector evolution_kernel(int num_qubits, const std::function> &, double)> &compute_step_matrix, const std::vector tlist, const std::vector>> &schedule_parameters); + /// Generates evolution kernels for the simulation. + static std::vector evolution_kernel( + int num_qubits, + const std::function> &, double)> + &compute_step_matrix, + const std::vector tlist, + const std::vector>> + &schedule_parameters); - /// Evolves a single quantum state under a given hamiltonian. - static evolve_result evolve_single(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, state initial_state, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); + /// Evolves a single quantum state under a given hamiltonian. + static evolve_result + evolve_single(const operator_sum &hamiltonian, + const std::map &dimensions, + const std::shared_ptr &schedule, state initial_state, + const std::vector &collapse_operators = {}, + const std::vector &observables = {}, + bool store_intermediate_results = false, + std::shared_ptr> integrator = nullptr, + std::optional shots_count = std::nullopt); - /// Evolves a single or multiple quantum states under a given hamiltonian. - static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, const std::vector &initial_states, const std::vector &collapse_operators = {}, const std::vector &observables = {}, bool store_intermediate_results = false, std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); + /// Evolves a single or multiple quantum states under a given hamiltonian. + static std::vector + evolve(const operator_sum &hamiltonian, const std::map &dimensions, + const std::shared_ptr &schedule, + const std::vector &initial_states, + const std::vector &collapse_operators = {}, + const std::vector &observables = {}, + bool store_intermediate_results = false, + std::shared_ptr> integrator = nullptr, + std::optional shots_count = std::nullopt); }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file From 943423827005b633914f72400cf4eaf800c2fb7b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 15 Jan 2025 08:58:11 -0800 Subject: [PATCH 130/311] Replacing Eigen::MatrixXcd with matrix_2 Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index c2915319e2..36b1afa4f5 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -12,8 +12,8 @@ #include "cudaq/base_integrator.h" #include "cudaq/operators.h" #include "cudaq/schedule.h" +#include "cudaq/utils/tensor.h" -#include #include #include #include @@ -23,11 +23,10 @@ namespace cudaq { class Evolution { public: /// Computes the Taylor series expansion of the matrix exponential. - static Eigen::MatrixXcd taylor_series_expm(const Eigen::MatrixXcd &op_matrix, - int order = 20); + static matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20); /// Computes the evolution step matrix - static Eigen::MatrixXcd compute_step_matrix( + static matrix_2 compute_step_matrix( const operator_sum &hamiltonian, const std::map &dimensions, const std::map> ¶meters, double dt, bool use_gpu = false); @@ -49,8 +48,8 @@ class Evolution { /// Generates evolution kernels for the simulation. static std::vector evolution_kernel( int num_qubits, - const std::function> &, double)> + const std::function< + matrix_2(const std::map> &, double)> &compute_step_matrix, const std::vector tlist, const std::vector>> From 824b8768e377c030d2ef21ba55ecfee14e5b2915 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 15 Jan 2025 16:25:04 -0800 Subject: [PATCH 131/311] * Adding Rydberg hamiltonian operator * Adding unitests for Rydberg hamiltonian operator * Making evaluate function in scalar_operator const Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 2 +- .../cudaq/dynamics/rydberg_hamiltonian.cpp | 55 ++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 2 +- runtime/cudaq/operators.h | 53 +++++++- unittests/CMakeLists.txt | 1 + unittests/dynamics/rydberg_hamiltonian.cpp | 124 ++++++++++++++++++ 6 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 runtime/cudaq/dynamics/rydberg_hamiltonian.cpp create mode 100644 unittests/dynamics/rydberg_hamiltonian.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index d608aba2c3..be4f65799c 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,7 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp ) add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) diff --git a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp new file mode 100644 index 0000000000..3d8b125ad3 --- /dev/null +++ b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include +#include + +namespace cudaq { +rydberg_hamiltonian::rydberg_hamiltonian( + const std::vector &atom_sites, const scalar_operator &litude, + const scalar_operator &phase, const scalar_operator &delta_global, + const std::vector &atom_filling, + const std::optional>> + &delta_local) + : atom_sites(atom_sites), amplitude(amplitude), phase(phase), + delta_global(delta_global), delta_local(delta_local) { + if (atom_filling.empty()) { + this->atom_filling = std::vector(atom_sites.size(), 1); + } else if (atom_sites.size() != atom_filling.size()) { + throw std::invalid_argument( + "Size of `atom_sites` and `atom_filling` must be equal."); + } else { + this->atom_filling = atom_filling; + } + + if (delta_local.has_value()) { + throw std::runtime_error( + "Local detuning is an experimental feature not yet supported."); + } +} + +const std::vector & +rydberg_hamiltonian::get_atom_sites() const { + return atom_sites; +} + +const std::vector &rydberg_hamiltonian::get_atom_filling() const { + return atom_filling; +} + +const scalar_operator &rydberg_hamiltonian::get_amplitude() const { + return amplitude; +} + +const scalar_operator &rydberg_hamiltonian::get_phase() const { return phase; } + +const scalar_operator &rydberg_hamiltonian::get_delta_global() const { + return delta_global; +} +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 1be54ea9ee..1d9716c9f3 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -40,7 +40,7 @@ scalar_operator::scalar_operator(double value) { } std::complex scalar_operator::evaluate( - std::map> parameters) { + std::map> parameters) const { return generator(parameters); } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d668909daa..121a088b49 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -64,8 +64,9 @@ class operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(const std::map &dimensions, - const std::map ¶ms = {}) const; + matrix_2 to_matrix( + const std::map &dimensions, + const std::map> ¶ms = {}) const; // Arithmetic operators operator_sum operator+(const operator_sum &other) const; @@ -398,7 +399,7 @@ class scalar_operator : public product_operator { /// @brief Return the scalar operator as a concrete complex value. std::complex - evaluate(std::map> parameters); + evaluate(std::map> parameters) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatability with the other inherited classes. @@ -461,4 +462,50 @@ void operator-=(scalar_operator &self, scalar_operator other); void operator*=(scalar_operator &self, scalar_operator other); void operator/=(scalar_operator &self, scalar_operator other); +/// @brief Representation of a time-dependent Hamiltonian for Rydberg system +class rydberg_hamiltonian : public operator_sum { +public: + using Coordinate = std::pair; + + /// @brief Constructor. + /// @param atom_sites List of 2D coordinates for trap sites. + /// @param amplitude Time-dependant driving amplitude, Omega(t). + /// @param phase Time-dependant driving phase, phi(t). + /// @param delta_global Time-dependant driving detuning, Delta_global(t). + /// @param atom_filling Optional. Marks occupied trap sites (1) and empty + /// sites (0). Defaults to all sites occupied. + /// @param delta_local Optional. A tuple of Delta_local(t) and site dependant + /// local detuning factors. + rydberg_hamiltonian( + const std::vector &atom_sites, + const scalar_operator &litude, const scalar_operator &phase, + const scalar_operator &delta_global, + const std::vector &atom_filling = {}, + const std::optional>> + &delta_local = std::nullopt); + + /// @brief Get atom sites. + const std::vector &get_atom_sites() const; + + /// @brief Get atom filling. + const std::vector &get_atom_filling() const; + + /// @brief Get amplitude operator. + const scalar_operator &get_amplitude() const; + + /// @brief Get phase operator. + const scalar_operator &get_phase() const; + + /// @brief Get global detuning operator. + const scalar_operator &get_delta_global() const; + +private: + std::vector atom_sites; + std::vector atom_filling; + scalar_operator amplitude; + scalar_operator phase; + scalar_operator delta_global; + std::optional>> delta_local; +}; + } // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f788ea0c48..f582c4d522 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -52,6 +52,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/test_runge_kutta_time_stepper.cpp dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp + dynamics/rydberg_hamiltonian.cpp ) # Make it so we can get function symbols diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp new file mode 100644 index 0000000000..2e64cb0666 --- /dev/null +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include + +using namespace cudaq; + +TEST(RydbergHamiltonianTest, ConstructorValidInputs) { + // Valid atom sites + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Valid atom filling + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + + EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); + EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); + EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), + std::complex(1.0, 0.0)); + EXPECT_EQ(hamiltonian.get_phase().evaluate({}), + std::complex(0.0, 0.0)); + EXPECT_EQ(hamiltonian.get_delta_global().evaluate({}), + std::complex(-0.5, 0.0)); +} + +TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Valid atom filling + std::vector atom_filling = {1, 0, 1}; + + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling); + + EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); + EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); +} + +TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Invalid atom filling size + std::vector atom_filling = {1, 0}; + + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling), + std::invalid_argument); +} + +TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + // Invalid delta_local + auto delta_local = + std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); + + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + {}, delta_local), + std::runtime_error); +} + +TEST(RydbergHamiltonianTest, Accessors) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + + EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); + EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), + std::complex(1.0, 0.0)); + EXPECT_EQ(hamiltonian.get_phase().evaluate({}), + std::complex(0.0, 0.0)); + EXPECT_EQ(hamiltonian.get_delta_global().evaluate({}), + std::complex(-0.5, 0.0)); +} + +TEST(RydbergHamiltonianTest, DefaultAtomFilling) { + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + + // Valid operators + scalar_operator amplitude(1.0); + scalar_operator phase(0.0); + scalar_operator delta_global(-0.5); + + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + + std::vector expected_filling(atom_sites.size(), 1); + EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); +} From 94f910e07892fa8b71f9e165ecf48d24123eadf2 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 16 Jan 2025 14:10:02 -0800 Subject: [PATCH 132/311] Adding interface for cudm_helpers Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 43 ++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 runtime/cudaq/cudm_helpers.h diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h new file mode 100644 index 0000000000..d706d5ed91 --- /dev/null +++ b/runtime/cudaq/cudm_helpers.h @@ -0,0 +1,43 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/utils/tensor.h" +#include +#include +#include +#include +#include + +namespace cudaq { +cudensitymatState_t initialize_state(cudensitymatHandle_t handle, + cudensitymatStatePurity_t purity, + int num_modes, + const std::vector &mode_extents); + +void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, + double scale_factor, cudaStream_t stream); + +void destroy_state(cudensitymatState_t state); + +cudensitymatOperator_t +compute_lindblad_operator(cudensitymatHandle_t handle, + const std::vector &c_ops, + const std::vector &mode_extents); + +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map ¶meters, const matrix_2 &matrix, + const std::vector &mode_extents); + +cudensitymatOperator_t construct_liovillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + double gamma); +} // namespace cudaq \ No newline at end of file From d8a990386985f0d23d174955ca75e82ea116105b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 16 Jan 2025 14:21:12 -0800 Subject: [PATCH 133/311] Changing matrix to operator_sum Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index d706d5ed91..6e1418f13e 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -9,6 +9,7 @@ #pragma once #include "cudaq/utils/tensor.h" +#include "cudaq/operators.h" #include #include #include @@ -33,7 +34,7 @@ compute_lindblad_operator(cudensitymatHandle_t handle, cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, - const std::map ¶meters, const matrix_2 &matrix, + const std::map ¶meters, const operator_sum &op, const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( From 8170e3206046c5e822298d9e5744626b2b117743 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 09:10:44 -0800 Subject: [PATCH 134/311] cudm_helpers implementation Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 8 +- runtime/cudaq/dynamics/CMakeLists.txt | 13 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 201 ++++++++++++++++++ runtime/cudaq/dynamics/cudm_state.h | 27 +++ .../cudaq/dynamics/elementary_operators.cpp | 6 +- runtime/cudaq/dynamics/evolution.cpp | 99 +++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 24 ++- runtime/cudaq/dynamics/product_operators.cpp | 47 ++++ runtime/cudaq/dynamics/scalar_operators.cpp | 4 +- runtime/cudaq/evolution.h | 9 +- runtime/cudaq/operators.h | 15 +- unittests/CMakeLists.txt | 15 +- unittests/dynamics/test_cudm_helpers.cpp | 111 ++++++++++ 13 files changed, 548 insertions(+), 31 deletions(-) create mode 100644 runtime/cudaq/dynamics/cudm_helpers.cpp create mode 100644 runtime/cudaq/dynamics/cudm_state.h create mode 100644 runtime/cudaq/dynamics/evolution.cpp create mode 100644 unittests/dynamics/test_cudm_helpers.cpp diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 6e1418f13e..de4b4760ab 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -8,8 +8,8 @@ #pragma once -#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" #include #include #include @@ -30,12 +30,12 @@ void destroy_state(cudensitymatState_t state); cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, - const std::vector &mode_extents); + const std::vector &mode_extents); cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, - const std::map ¶meters, const operator_sum &op, - const std::vector &mode_extents); + const std::map> ¶meters, + const operator_sum &op, const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index be4f65799c..283e77e7ff 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -7,22 +7,31 @@ # ============================================================================ # set(LIBRARY_NAME cudaq-operators) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp ) +set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") +if (NOT DEFINED CUQUANTUM_INSTALL_PREFIX) + message(FATAL_ERROR "CUQUANTUM_INSTALL_PREFIX is not defined.") +endif() + add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) target_include_directories(${LIBRARY_NAME} PUBLIC $ $ + $ + /usr/local/cuda/targets/x86_64-linux/include $ PRIVATE .) +target_link_libraries(${LIBRARY_NAME} PRIVATE ${CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0) + set (OPERATOR_DEPENDENCIES "") list(APPEND OPERATOR_DEPENDENCIES fmt::fmt-header-only) add_openmp_configurations(${LIBRARY_NAME} OPERATOR_DEPENDENCIES) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp new file mode 100644 index 0000000000..c9248720f9 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -0,0 +1,201 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_helpers.h" + +namespace cudaq { +cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents) { + try { + cudensitymatState_t state; + auto status = cudensitymatCreateState(handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to initialize quantum state."); + } + return state; + } catch (const std::exception &e) { + std::cerr << "Error in initialize_state: " << e.what() < &c_ops, const std::vector &mode_extents) { + try { + if (c_ops.empty()) { + throw std::invalid_argument("Collapse operators cannot be empty."); + } + + cudensitymatOperator_t lindblad_op; + auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &lindblad_op); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create lindblad operator."); + } + + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument("Collapse operator must be a square matrix."); + } + + std::vector> flat_matrix(dim * dim); + for (size_t i = 0; i < dim; i++) { + for (size_t j = 0; j < dim; j++) { + flat_matrix[i * dim + j] = c_op[{i, j}]; + } + } + + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(lindblad_op); + throw std::runtime_error("Failed to create operator term."); + } + + // Attach terms and cleanup + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to append operator term."); + } + + cudensitymatDestroyOperatorTerm(term); + } + + return lindblad_op; + } catch (const std::exception &e) { + std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; + throw; + } +} + +cudensitymatOperator_t convert_to_cudensitymat_operator(cudensitymatHandle_t handle, const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { + try { + cudensitymatOperator_t operator_handle; + auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &operator_handle); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create operator."); + } + + // Define dimensions for the operator + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } + + auto matrix = op.to_matrix(dimensions, parameters); + size_t dim = matrix.get_rows(); + if (matrix.get_columns() != dim) { + throw std::invalid_argument("Matrix must be a square."); + } + + std::vector> flat_matrix; + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } + + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to create operator term."); + } + + // Attach flat_matrix to the term + int32_t num_elem_operators = 1; + int32_t num_operator_modes = static_cast(mode_extents.size()); + const int64_t *operator_mode_extents = mode_extents.data(); + const int64_t *operator_mode_strides = nullptr; + int32_t state_modes_acted_on[static_cast(mode_extents.size())]; + for (int32_t i = 0; i < num_operator_modes; i++) { + state_modes_acted_on[i] = i; + } + + cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + + void *tensor_data = flat_matrix.data(); + cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + + status = cudensitymatOperatorTermAppendGeneralProduct(handle, term, + num_elem_operators, &num_operator_modes, &operator_mode_extents, + &operator_mode_strides, state_modes_acted_on, nullptr, + CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to attach flat_matrix to operator term."); + } + + status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to attach term to operator."); + } + + cudensitymatDestroyOperatorTerm(term); + return operator_handle; + } catch (const std::exception &e) { + std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() << std::endl; + throw; + } +} + +cudensitymatOperator_t construct_liovillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { + try { + cudensitymatOperator_t liouvillian; + auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create Liouvillian operator."); + } + + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add hamiltonian term."); + } + + cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); + for (const auto &c_op : collapse_operators) { + status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add collapse operator term."); + } + } + + return liouvillian; + } catch (const std::exception &e) { + std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; + throw; + } +} +} diff --git a/runtime/cudaq/dynamics/cudm_state.h b/runtime/cudaq/dynamics/cudm_state.h new file mode 100644 index 0000000000..1bdba17782 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_state.h @@ -0,0 +1,27 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include + +namespace cudaq { +class cudm_mat_state { +public: + cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents); + ~cudm_mat_state(); + + void scale(double factor, cudaStream_t stream); + cudensitymatState_t get() const; + +private: + cudensitymatState_t state; + cudensitymatHandle_t handle; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 137dde02cc..574891033f 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -280,9 +280,9 @@ elementary_operator::squeeze(int degree, std::complex amplitude) { } matrix_2 elementary_operator::to_matrix( - std::map dimensions, - std::map> parameters) { - return m_ops[id].generator(dimensions, parameters); + const std::map dimensions, + const std::map> parameters) const { + return m_ops.at(id).generator(dimensions, parameters); } /// Elementary Operator Arithmetic. diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp new file mode 100644 index 0000000000..69a8209607 --- /dev/null +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -0,0 +1,99 @@ +// /****************************************************************-*- C++ -*-**** +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * +// ******************************************************************************/ + +// #include "cudaq/evolution.h" +// #include +// #include +// #include +// #include + +// namespace cudaq { + +// // Can be removed +// matrix_2 taylor_series_expm(const matrix_2 &op_matrix, +// int order = 20) { +// matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); +// matrix_2 op_matrix_n = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + +// for (size_t i = 0; i < op_matrix.get_rows(); i++) { +// result[{i, i}] = std::complex(1.0, 0.0); +// op_matrix_n[{i, i}] = std::complex(1.0, 0.0); +// } + +// double factorial = 1.0; +// for (int n = 1; n <= order; n++) { +// op_matrix_n *= op_matrix; +// factorial *= n; +// result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; +// } + +// return result; +// } + +// matrix_2 compute_step_matrix( +// const operator_sum &hamiltonian, const std::map &dimensions, +// const std::map> ¶meters, double dt, +// bool use_gpu) { +// matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); +// op_matrix = dt * std::complex(0, -1) * op_matrix; + +// if (use_gpu) { +// // TODO: Implement GPU matrix exponential using CuPy or cuQuantum +// throw std::runtime_error("GPU-based matrix exponentiation not implemented."); +// } else { +// return taylor_series_expm(op_matrix); +// } +// } + +// void add_noise_channel_for_step( +// const std::string &step_kernel_name, cudaq::noise_model &noise_model, +// const std::vector &collapse_operators, +// const std::map &dimensions, +// const std::map> ¶meters, double dt) { +// for (const auto &collapse_op : collapse_operators) { +// matrix_2 L = collapse_op.to_matrix(dimensions, parameters); +// matrix_2 G = std::complex(-0.5, 0.0) * (L * L); + +// // Kraus operators +// matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); +// matrix_2 M1 = std::sqrt(dt) * L; + +// try { +// noise_model.add_all_qubit_channel(step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); +// } catch (const std::exception &e) { +// std::cerr << "Error adding noise channel: " << e.what() << std::endl; +// throw; +// } +// } +// } + +// // evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, +// // const rydberg_hamiltonian &hamiltonian, +// // const Schedule &schedule, +// // int shots_count, bool is_async = false) { +// // // Generate the time series +// // std::vector> amp_ts, ph_ts, dg_ts; + +// // auto current_schedule = schedule; +// // current_schedule.reset(); + +// // while(auto t = current_schedule.current_step()) { +// // std::map> parameters = {{"time", t.value()}}; + +// // amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), t.value().real()); +// // ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), t.value().real()); +// // dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), t.value().real()); + +// // ++schedule; +// // } + +// // // Atom arrangement and physical fields +// // cudaq::ahs::AtomArrangement atoms; + +// // } +// } \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index a0ba70cb2b..dd3227784d 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -347,12 +347,24 @@ operator_sum operator_sum::operator*=(const elementary_operator &other) { return *this; } -/// FIXME: -// tensor -// operator_sum::to_matrix(const std::map &dimensions, -// const std::map ¶ms) const { -// // todo -// } +matrix_2 operator_sum::to_matrix( + const std::map &dimensions, + const std::map> ¶ms) const { + std::size_t total_dimension = 1; + for (const auto &[_, dim] : dimensions) { + total_dimension *= dim; + } + + matrix_2 result(total_dimension, total_dimension); + + for (const auto &term : m_terms) { + matrix_2 term_matrix = term.to_matrix(dimensions, params); + + result += term_matrix; + } + + return result; +} // std::string operator_sum::to_string() const { // std::string result; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 0c8b411b1a..32adcaa339 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -35,6 +35,53 @@ product_operator::product_operator( // tensor::identity(1, 1), kronecker); // } +matrix_2 product_operator::to_matrix( + const std::map dimensions, + const std::map> parameters) const { + // Lambda functions to retrieve degrees and matrices + auto getDegrees = [](auto &&term) { return term.degrees; }; + auto getMatrix = [&](auto &&term) { + return term.to_matrix(dimensions, parameters); + }; + + // Initialize a result matrix with a single identity element + matrix_2 result(1, 1); + result[{0, 0}] = 1.0; + + // Iterate over all terms in the product operator + for (const auto &term : m_terms) { + // Get the degrees for the current term + auto termDegrees = std::visit(getDegrees, term); + bool inserted = false; + + matrix_2 termMatrix(1, 1); + termMatrix[{0, 0}] = 1.0; + + // Build the matrix list with identities or operator matrices + for (const auto &[degree, dim] : dimensions) { + if (std::find(termDegrees.begin(), termDegrees.end(), degree) != + termDegrees.end() && + !inserted) { + // Use the operator matrix for the active degree + termMatrix.kronecker_inplace(std::visit(getMatrix, term)); + inserted = true; + } else { + // Use identity matrix for other degrees + matrix_2 identityMatrix(dim, dim); + for (std::size_t i = 0; i < dim; i++) { + identityMatrix[{i, i}] = 1.0; + } + termMatrix.kronecker_inplace(identityMatrix); + } + } + + // Multiply the result matrix by the term matrix + result *= termMatrix; + } + + return result; +} + // /// IMPLEMENT: // tensor product_operator::to_matrix( // std::map dimensions, diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 1d9716c9f3..4d640b50b5 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -45,8 +45,8 @@ std::complex scalar_operator::evaluate( } matrix_2 scalar_operator::to_matrix( - std::map dimensions, - std::map> parameters) { + const std::map dimensions, + const std::map> parameters) const { auto returnOperator = matrix_2(1, 1); returnOperator[{0, 0}] = evaluate(parameters); return returnOperator; diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 36b1afa4f5..7b5f717806 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -39,11 +39,9 @@ class Evolution { const std::map> ¶meters, double dt); /// Launches an analog Hamiltonian kernel for quantum simulations. - static evolve_result - launch_analog_hamiltonian_kernel(const std::string &target_name, - const operator_sum &hamiltonian, - const std::shared_ptr &schedule, - int shots_count, bool is_async = false); + static evolve_result launch_analog_hamiltonian_kernel( + const std::string &target_name, const rydberg_hamiltonian &hamiltonian, + const Schedule &schedule, int shots_count, bool is_async = false); /// Generates evolution kernels for the simulation. static std::vector evolution_kernel( @@ -67,6 +65,7 @@ class Evolution { std::optional shots_count = std::nullopt); /// Evolves a single or multiple quantum states under a given hamiltonian. + /// Run only for dynamics target else throw error static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, const std::shared_ptr &schedule, diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 121a088b49..8924dbb861 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -200,8 +200,9 @@ class product_operator : public operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); + matrix_2 + to_matrix(const std::map dimensions, + const std::map> parameters) const; /// @brief Creates a representation of the operator as a `cudaq::pauli_word` /// that can be passed as an argument to quantum kernels. @@ -279,8 +280,9 @@ class elementary_operator : public product_operator { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); + matrix_2 + to_matrix(const std::map dimensions, + const std::map> parameters) const; // Predefined operators. static elementary_operator identity(int degree); @@ -403,8 +405,9 @@ class scalar_operator : public product_operator { // Return the scalar operator as a 1x1 matrix. This is needed for // compatability with the other inherited classes. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); + matrix_2 + to_matrix(const std::map dimensions, + const std::map> parameters) const; // /// @brief Returns true if other is a scalar operator with the same // /// generator. diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f582c4d522..bc1d391943 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -53,11 +53,14 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp + dynamics/test_cudm_helpers.cpp ) # Make it so we can get function symbols set (CMAKE_ENABLE_EXPORTS TRUE) +include_directories(/usr/local/cuda/targets/x86_64-linux/include) + ## This Macro allows us to create a test_runtime executable for ## the sources in CUDAQ_RUNTIME_TEST_SOURCE for a specific backend simulator macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) @@ -80,7 +83,9 @@ macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) cudaq fmt::fmt-header-only cudaq-platform-default cudaq-builder - gtest_main) + gtest_main + $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 + /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) set(TEST_LABELS "") if (${NVQIR_BACKEND} STREQUAL "qpp") target_compile_definitions(${TEST_EXE_NAME} PRIVATE -DCUDAQ_SIMULATION_SCALAR_FP64) @@ -275,6 +280,7 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_ops_simple.cpp dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp + dynamics/test_cudm_helpers.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) @@ -285,7 +291,9 @@ target_link_libraries(test_operators cudaq-spin cudaq-operators cudaq - gtest_main) + gtest_main + $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 + /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) gtest_discover_tests(test_operators) add_subdirectory(plugin) @@ -406,7 +414,8 @@ target_link_libraries(${TEST_EXE_NAME} cudaq-platform-default cudaq-rest-qpu cudaq-builder - gtest_main) + gtest_main + $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0) set(TEST_LABELS "") if ("${TEST_LABELS}" STREQUAL "") gtest_discover_tests(${TEST_EXE_NAME}) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp new file mode 100644 index 0000000000..4e1a21306e --- /dev/null +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include + +// Initialize operator_sum +cudaq::operator_sum initialize_operator_sum() { + std::vector degrees = {0, 1}; + + // Elementary operators + cudaq::elementary_operator pauli_x("pauli_x", {0}); + cudaq::elementary_operator pauli_z("pauli_z", {1}); + cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); + + auto prod_op_1 = + cudaq::scalar_operator(std::complex(1.0, 0.0)) * pauli_x * pauli_z; + + auto prod_op_2 = + cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; + + cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + + return op_sum; +} + +class CuDensityMatTestFixture : public ::testing::Test { +protected: + cudensitymatHandle_t handle; + + void SetUp() override { + auto status = cudensitymatCreate(&handle); + ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); + } + + void TearDown() override { + cudensitymatDestroy(handle); + } +}; + +// Test for convert_to_cudensitymat_operator +TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { + std::vector mode_extents = {2, 2}; + + auto op_sum = initialize_operator_sum(); + + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + + ASSERT_NE(result, nullptr); + + cudensitymatDestroyOperator(result); +} + +// Test for compute_lindblad_op +TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { + std::vector mode_extents = {2, 2}; + + cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); + cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); + std::vector c_ops = {c_op1, c_op2}; + + auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + + ASSERT_NE(result, nullptr); + + cudensitymatDestroyOperator(result); +} + +// Test for initialize_state +TEST_F(CuDensityMatTestFixture, InitializeState) { + std::vector mode_extents = {2, 2}; + + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); + + ASSERT_NE(state, nullptr); + + cudaq::destroy_state(state); +} + +// Test for scale_state +TEST_F(CuDensityMatTestFixture, ScaleState) { + std::vector mode_extents = {2, 2}; + + ASSERT_NO_THROW({ + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); + ASSERT_NE(state, nullptr); + + cudaStream_t stream; + cudaStreamCreate(&stream); + + EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + + cudaStreamDestroy(stream); + cudaq::destroy_state(state); + }); +} + +// Test invalid handle +TEST_F(CuDensityMatTestFixture, InvalidHandle) { + cudensitymatHandle_t invalid_handle = nullptr; + + std::vector mode_extents = {2, 2}; + auto op_sum = initialize_operator_sum(); + + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, op_sum, mode_extents), std::runtime_error); +} From 5aeb4d7e5442f09d4e41bf985dc2c0ec40a0e6de Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 09:13:36 -0800 Subject: [PATCH 135/311] Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 355 ++++++++++++----------- runtime/cudaq/dynamics/cudm_state.h | 15 +- runtime/cudaq/dynamics/evolution.cpp | 197 +++++++------ unittests/dynamics/test_cudm_helpers.cpp | 101 +++---- 4 files changed, 355 insertions(+), 313 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index c9248720f9..9689d06975 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -9,193 +9,224 @@ #include "cudaq/cudm_helpers.h" namespace cudaq { -cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents) { - try { - cudensitymatState_t state; - auto status = cudensitymatCreateState(handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to initialize quantum state."); - } - return state; - } catch (const std::exception &e) { - std::cerr << "Error in initialize_state: " << e.what() < &mode_extents) { + try { + cudensitymatState_t state; + auto status = cudensitymatCreateState( + handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to initialize quantum state."); } + return state; + } catch (const std::exception &e) { + std::cerr << "Error in initialize_state: " << e.what() << std::endl; + throw; + } } -void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { - try { - auto status = cudensitymatStateComputeScaling(handle, state, &scale_factor, stream); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to scale quantum state."); - } - } catch (const std::exception &e) { - std::cerr << "Error in scale_state: " << e.what() < &c_ops, const std::vector &mode_extents) { - try { - if (c_ops.empty()) { - throw std::invalid_argument("Collapse operators cannot be empty."); - } - - cudensitymatOperator_t lindblad_op; - auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &lindblad_op); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create lindblad operator."); - } - - for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument("Collapse operator must be a square matrix."); - } - - std::vector> flat_matrix(dim * dim); - for (size_t i = 0; i < dim; i++) { - for (size_t j = 0; j < dim; j++) { - flat_matrix[i * dim + j] = c_op[{i, j}]; - } - } - - // Create Operator term for LtL and add to lindblad_op - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(lindblad_op); - throw std::runtime_error("Failed to create operator term."); - } - - // Attach terms and cleanup - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to append operator term."); - } - - cudensitymatDestroyOperatorTerm(term); - } - - return lindblad_op; - } catch (const std::exception &e) { - std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; - throw; +cudensitymatOperator_t +compute_lindblad_operator(cudensitymatHandle_t handle, + const std::vector &c_ops, + const std::vector &mode_extents) { + try { + if (c_ops.empty()) { + throw std::invalid_argument("Collapse operators cannot be empty."); } -} -cudensitymatOperator_t convert_to_cudensitymat_operator(cudensitymatHandle_t handle, const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { - try { - cudensitymatOperator_t operator_handle; - auto status = cudensitymatCreateOperator(handle, static_cast(mode_extents.size()), mode_extents.data(), &operator_handle); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create operator."); - } + cudensitymatOperator_t lindblad_op; + auto status = cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &lindblad_op); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create lindblad operator."); + } - // Define dimensions for the operator - std::map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument( + "Collapse operator must be a square matrix."); + } + + std::vector> flat_matrix(dim * dim); + for (size_t i = 0; i < dim; i++) { + for (size_t j = 0; j < dim; j++) { + flat_matrix[i * dim + j] = c_op[{i, j}]; + } + } + + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(lindblad_op); + throw std::runtime_error("Failed to create operator term."); + } + + // Attach terms and cleanup + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, + {1.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to append operator term."); + } + + cudensitymatDestroyOperatorTerm(term); + } - auto matrix = op.to_matrix(dimensions, parameters); - size_t dim = matrix.get_rows(); - if (matrix.get_columns() != dim) { - throw std::invalid_argument("Matrix must be a square."); - } + return lindblad_op; + } catch (const std::exception &e) { + std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; + throw; + } +} - std::vector> flat_matrix; - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); - } - } +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map> ¶meters, + const operator_sum &op, const std::vector &mode_extents) { + try { + cudensitymatOperator_t operator_handle; + auto status = cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &operator_handle); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create operator."); + } - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm(handle, static_cast(mode_extents.size()), mode_extents.data(), &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to create operator term."); - } + // Define dimensions for the operator + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } - // Attach flat_matrix to the term - int32_t num_elem_operators = 1; - int32_t num_operator_modes = static_cast(mode_extents.size()); - const int64_t *operator_mode_extents = mode_extents.data(); - const int64_t *operator_mode_strides = nullptr; - int32_t state_modes_acted_on[static_cast(mode_extents.size())]; - for (int32_t i = 0; i < num_operator_modes; i++) { - state_modes_acted_on[i] = i; - } + auto matrix = op.to_matrix(dimensions, parameters); + size_t dim = matrix.get_rows(); + if (matrix.get_columns() != dim) { + throw std::invalid_argument("Matrix must be a square."); + } - cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + std::vector> flat_matrix; + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } - void *tensor_data = flat_matrix.data(); - cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + cudensitymatOperatorTerm_t term; + status = cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to create operator term."); + } - status = cudensitymatOperatorTermAppendGeneralProduct(handle, term, - num_elem_operators, &num_operator_modes, &operator_mode_extents, - &operator_mode_strides, state_modes_acted_on, nullptr, - CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to attach flat_matrix to operator term."); - } + // Attach flat_matrix to the term + int32_t num_elem_operators = 1; + int32_t num_operator_modes = static_cast(mode_extents.size()); + const int64_t *operator_mode_extents = mode_extents.data(); + const int64_t *operator_mode_strides = nullptr; + int32_t state_modes_acted_on[static_cast(mode_extents.size())]; + for (int32_t i = 0; i < num_operator_modes; i++) { + state_modes_acted_on[i] = i; + } - status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to attach term to operator."); - } + cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + + void *tensor_data = flat_matrix.data(); + cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + + status = cudensitymatOperatorTermAppendGeneralProduct( + handle, term, num_elem_operators, &num_operator_modes, + &operator_mode_extents, &operator_mode_strides, state_modes_acted_on, + nullptr, CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, + scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error( + "Failed to attach flat_matrix to operator term."); + } - cudensitymatDestroyOperatorTerm(term); - return operator_handle; - } catch (const std::exception &e) { - std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() << std::endl; - throw; + status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, + coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyOperator(operator_handle); + throw std::runtime_error("Failed to attach term to operator."); } -} -cudensitymatOperator_t construct_liovillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { - try { - cudensitymatOperator_t liouvillian; - auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create Liouvillian operator."); - } + cudensitymatDestroyOperatorTerm(term); + return operator_handle; + } catch (const std::exception &e) { + std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() + << std::endl; + throw; + } +} - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add hamiltonian term."); - } +cudensitymatOperator_t construct_liovillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + double gamma) { + try { + cudensitymatOperator_t liouvillian; + auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + throw std::runtime_error("Failed to create Liouvillian operator."); + } - cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); - for (const auto &c_op : collapse_operators) { - status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add collapse operator term."); - } - } + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, + {1.0, 0.0}, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add hamiltonian term."); + } - return liouvillian; - } catch (const std::exception &e) { - std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; - throw; + cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); + for (const auto &c_op : collapse_operators) { + status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, + coefficient, scalarCallback); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + cudensitymatDestroyOperator(liouvillian); + throw std::runtime_error("Failed to add collapse operator term."); + } } + + return liouvillian; + } catch (const std::exception &e) { + std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; + throw; + } } -} +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_state.h b/runtime/cudaq/dynamics/cudm_state.h index 1bdba17782..1f20f9483f 100644 --- a/runtime/cudaq/dynamics/cudm_state.h +++ b/runtime/cudaq/dynamics/cudm_state.h @@ -14,14 +14,15 @@ namespace cudaq { class cudm_mat_state { public: - cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, int num_modes, const std::vector &mode_extents); - ~cudm_mat_state(); + cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, + int num_modes, const std::vector &mode_extents); + ~cudm_mat_state(); - void scale(double factor, cudaStream_t stream); - cudensitymatState_t get() const; + void scale(double factor, cudaStream_t stream); + cudensitymatState_t get() const; private: - cudensitymatState_t state; - cudensitymatHandle_t handle; + cudensitymatState_t state; + cudensitymatHandle_t handle; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 69a8209607..75e5ca0875 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -1,99 +1,106 @@ -// /****************************************************************-*- C++ -*-**** -// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * -// * All rights reserved. * -// * * -// * This source code and the accompanying materials are made available under * -// * the terms of the Apache License 2.0 which accompanies this distribution. * -// ******************************************************************************/ - -// #include "cudaq/evolution.h" -// #include -// #include -// #include -// #include - -// namespace cudaq { - -// // Can be removed -// matrix_2 taylor_series_expm(const matrix_2 &op_matrix, -// int order = 20) { -// matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); -// matrix_2 op_matrix_n = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); - -// for (size_t i = 0; i < op_matrix.get_rows(); i++) { -// result[{i, i}] = std::complex(1.0, 0.0); -// op_matrix_n[{i, i}] = std::complex(1.0, 0.0); +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/evolution.h" +#include +#include +#include +#include + +namespace cudaq { + +// Can be removed +matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20) { + matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + matrix_2 op_matrix_n = + matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + + for (size_t i = 0; i < op_matrix.get_rows(); i++) { + result[{i, i}] = std::complex(1.0, 0.0); + op_matrix_n[{i, i}] = std::complex(1.0, 0.0); + } + + double factorial = 1.0; + for (int n = 1; n <= order; n++) { + op_matrix_n *= op_matrix; + factorial *= n; + result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; + } + + return result; +} + +matrix_2 compute_step_matrix( + const operator_sum &hamiltonian, const std::map &dimensions, + const std::map> ¶meters, double dt, + bool use_gpu) { + matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); + op_matrix = dt * std::complex(0, -1) * op_matrix; + + if (use_gpu) { + // TODO: Implement GPU matrix exponential using CuPy or cuQuantum + throw std::runtime_error( + "GPU-based matrix exponentiation not implemented."); + } else { + return taylor_series_expm(op_matrix); + } +} + +void add_noise_channel_for_step( + const std::string &step_kernel_name, cudaq::noise_model &noise_model, + const std::vector &collapse_operators, + const std::map &dimensions, + const std::map> ¶meters, double dt) { + for (const auto &collapse_op : collapse_operators) { + matrix_2 L = collapse_op.to_matrix(dimensions, parameters); + matrix_2 G = std::complex(-0.5, 0.0) * (L * L); + + // Kraus operators + matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); + matrix_2 M1 = std::sqrt(dt) * L; + + try { + noise_model.add_all_qubit_channel( + step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); + } catch (const std::exception &e) { + std::cerr << "Error adding noise channel: " << e.what() << std::endl; + throw; + } + } +} + +// evolve_result launch_analog_hamiltonian_kernel(const std::string +// &target_name, +// const rydberg_hamiltonian &hamiltonian, +// const Schedule &schedule, +// int shots_count, bool is_async = false) { +// // Generate the time series +// std::vector> amp_ts, ph_ts, dg_ts; + +// auto current_schedule = schedule; +// current_schedule.reset(); + +// while(auto t = current_schedule.current_step()) { +// std::map> parameters = {{"time", +// t.value()}}; + +// amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), +// t.value().real()); +// ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), +// t.value().real()); +// dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), +// t.value().real()); + +// ++schedule; // } -// double factorial = 1.0; -// for (int n = 1; n <= order; n++) { -// op_matrix_n *= op_matrix; -// factorial *= n; -// result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; -// } - -// return result; -// } +// // Atom arrangement and physical fields +// cudaq::ahs::AtomArrangement atoms; -// matrix_2 compute_step_matrix( -// const operator_sum &hamiltonian, const std::map &dimensions, -// const std::map> ¶meters, double dt, -// bool use_gpu) { -// matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); -// op_matrix = dt * std::complex(0, -1) * op_matrix; - -// if (use_gpu) { -// // TODO: Implement GPU matrix exponential using CuPy or cuQuantum -// throw std::runtime_error("GPU-based matrix exponentiation not implemented."); -// } else { -// return taylor_series_expm(op_matrix); -// } -// } - -// void add_noise_channel_for_step( -// const std::string &step_kernel_name, cudaq::noise_model &noise_model, -// const std::vector &collapse_operators, -// const std::map &dimensions, -// const std::map> ¶meters, double dt) { -// for (const auto &collapse_op : collapse_operators) { -// matrix_2 L = collapse_op.to_matrix(dimensions, parameters); -// matrix_2 G = std::complex(-0.5, 0.0) * (L * L); - -// // Kraus operators -// matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); -// matrix_2 M1 = std::sqrt(dt) * L; - -// try { -// noise_model.add_all_qubit_channel(step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); -// } catch (const std::exception &e) { -// std::cerr << "Error adding noise channel: " << e.what() << std::endl; -// throw; -// } -// } // } - -// // evolve_result launch_analog_hamiltonian_kernel(const std::string &target_name, -// // const rydberg_hamiltonian &hamiltonian, -// // const Schedule &schedule, -// // int shots_count, bool is_async = false) { -// // // Generate the time series -// // std::vector> amp_ts, ph_ts, dg_ts; - -// // auto current_schedule = schedule; -// // current_schedule.reset(); - -// // while(auto t = current_schedule.current_step()) { -// // std::map> parameters = {{"time", t.value()}}; - -// // amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), t.value().real()); -// // ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), t.value().real()); -// // dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), t.value().real()); - -// // ++schedule; -// // } - -// // // Atom arrangement and physical fields -// // cudaq::ahs::AtomArrangement atoms; - -// // } -// } \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 4e1a21306e..4ec8f100ba 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -11,101 +11,104 @@ // Initialize operator_sum cudaq::operator_sum initialize_operator_sum() { - std::vector degrees = {0, 1}; + std::vector degrees = {0, 1}; - // Elementary operators - cudaq::elementary_operator pauli_x("pauli_x", {0}); - cudaq::elementary_operator pauli_z("pauli_z", {1}); - cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); + // Elementary operators + cudaq::elementary_operator pauli_x("pauli_x", {0}); + cudaq::elementary_operator pauli_z("pauli_z", {1}); + cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); - auto prod_op_1 = - cudaq::scalar_operator(std::complex(1.0, 0.0)) * pauli_x * pauli_z; + auto prod_op_1 = cudaq::scalar_operator(std::complex(1.0, 0.0)) * + pauli_x * pauli_z; - auto prod_op_2 = - cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; + auto prod_op_2 = + cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; - cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); - return op_sum; + return op_sum; } class CuDensityMatTestFixture : public ::testing::Test { protected: - cudensitymatHandle_t handle; + cudensitymatHandle_t handle; - void SetUp() override { - auto status = cudensitymatCreate(&handle); - ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); - } + void SetUp() override { + auto status = cudensitymatCreate(&handle); + ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); + } - void TearDown() override { - cudensitymatDestroy(handle); - } + void TearDown() override { cudensitymatDestroy(handle); } }; // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); - auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + auto result = + cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); + ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); + cudensitymatDestroyOperator(result); } // Test for compute_lindblad_op TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); - cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); - std::vector c_ops = {c_op1, c_op2}; + cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); + cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); + std::vector c_ops = {c_op1, c_op2}; - auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - ASSERT_NE(result, nullptr); + ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); + cudensitymatDestroyOperator(result); } // Test for initialize_state TEST_F(CuDensityMatTestFixture, InitializeState) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + 2, mode_extents); - ASSERT_NE(state, nullptr); + ASSERT_NE(state, nullptr); - cudaq::destroy_state(state); + cudaq::destroy_state(state); } // Test for scale_state TEST_F(CuDensityMatTestFixture, ScaleState) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2, 2}; - ASSERT_NO_THROW({ - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, 2, mode_extents); - ASSERT_NE(state, nullptr); + ASSERT_NO_THROW({ + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + 2, mode_extents); + ASSERT_NE(state, nullptr); - cudaStream_t stream; - cudaStreamCreate(&stream); + cudaStream_t stream; + cudaStreamCreate(&stream); - EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); - cudaStreamDestroy(stream); - cudaq::destroy_state(state); - }); + cudaStreamDestroy(stream); + cudaq::destroy_state(state); + }); } // Test invalid handle TEST_F(CuDensityMatTestFixture, InvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; + cudensitymatHandle_t invalid_handle = nullptr; - std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + std::vector mode_extents = {2, 2}; + auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, op_sum, mode_extents), std::runtime_error); + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, + op_sum, mode_extents), + std::runtime_error); } From 664515d398270d41b3e49ba68bc20ac7d1f95414 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 14:37:29 -0800 Subject: [PATCH 136/311] * Adding macro for handling error * Using CUDA_C_64F data type for complex Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_error_handling.h | 29 +++++++++++++++++++++++++ runtime/cudaq/cudm_helpers.h | 1 - runtime/cudaq/dynamics/cudm_helpers.cpp | 9 +++----- 3 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 runtime/cudaq/cudm_error_handling.h diff --git a/runtime/cudaq/cudm_error_handling.h b/runtime/cudaq/cudm_error_handling.h new file mode 100644 index 0000000000..d1b8ac734f --- /dev/null +++ b/runtime/cudaq/cudm_error_handling.h @@ -0,0 +1,29 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include + +#define HANDLE_CUDM_ERROR(x) \ +{ \ + const auto err = x; \ + if (err != CUDENSITYMAT_STATUS_SUCCESS) { \ + throw std::runtime_error(fmt::format("[cudaq] %{} in {} (line {})", err \ + __FUNCTION__, __LINE__)); \ + } \ +} + +#define HANDLE_CUDA_ERROR(x) \ +{ \ + const auto err = x; \ + if (err != cudaSuccess) { \ + throw std::runtime_error(fmt::format("[cuda] %{} in {} (line {})", err \ + __FUNCTION__, __LINE__)); \ + } \ +} \ No newline at end of file diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index de4b4760ab..c2968229fb 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -19,7 +19,6 @@ namespace cudaq { cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, - int num_modes, const std::vector &mode_extents); void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 9689d06975..785057fbc6 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -11,15 +11,12 @@ namespace cudaq { cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, - int num_modes, const std::vector &mode_extents) { try { cudensitymatState_t state; - auto status = cudensitymatCreateState( - handle, purity, num_modes, mode_extents.data(), 1, CUDA_R_64F, &state); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to initialize quantum state."); - } + HANDLE_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), + mode_extents.data(), 1, CUDA_C_64F, + &state)); return state; } catch (const std::exception &e) { std::cerr << "Error in initialize_state: " << e.what() << std::endl; From 8058b27c343d4e1a2daae94d625c160546fa1353 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 21 Jan 2025 18:09:29 -0800 Subject: [PATCH 137/311] Adding MACRO for error handling Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_error_handling.h | 33 +++---- runtime/cudaq/dynamics/cudm_helpers.cpp | 110 ++++++++--------------- unittests/dynamics/test_cudm_helpers.cpp | 4 +- 3 files changed, 58 insertions(+), 89 deletions(-) diff --git a/runtime/cudaq/cudm_error_handling.h b/runtime/cudaq/cudm_error_handling.h index d1b8ac734f..d72be33906 100644 --- a/runtime/cudaq/cudm_error_handling.h +++ b/runtime/cudaq/cudm_error_handling.h @@ -8,22 +8,23 @@ #pragma once #include +#include #include -#define HANDLE_CUDM_ERROR(x) \ -{ \ - const auto err = x; \ - if (err != CUDENSITYMAT_STATUS_SUCCESS) { \ - throw std::runtime_error(fmt::format("[cudaq] %{} in {} (line {})", err \ - __FUNCTION__, __LINE__)); \ - } \ -} +#define HANDLE_CUDM_ERROR(x) \ + { \ + const auto err = x; \ + if (err != CUDENSITYMAT_STATUS_SUCCESS) { \ + throw std::runtime_error(fmt::format("[cudaq] %{} in {} (line {})", err, \ + __FUNCTION__, __LINE__)); \ + } \ + } -#define HANDLE_CUDA_ERROR(x) \ -{ \ - const auto err = x; \ - if (err != cudaSuccess) { \ - throw std::runtime_error(fmt::format("[cuda] %{} in {} (line {})", err \ - __FUNCTION__, __LINE__)); \ - } \ -} \ No newline at end of file +#define HANDLE_CUDA_ERROR(x) \ + { \ + const auto err = x; \ + if (err != cudaSuccess) { \ + throw std::runtime_error(fmt::format("[cuda] %{} in {} (line {})", err, \ + __FUNCTION__, __LINE__)); \ + } \ + } diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 785057fbc6..914a02914b 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -7,102 +7,70 @@ ******************************************************************************/ #include "cudaq/cudm_helpers.h" +#include "cudaq/cudm_error_handling.h" namespace cudaq { cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, const std::vector &mode_extents) { - try { - cudensitymatState_t state; - HANDLE_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), - mode_extents.data(), 1, CUDA_C_64F, - &state)); - return state; - } catch (const std::exception &e) { - std::cerr << "Error in initialize_state: " << e.what() << std::endl; - throw; - } + cudensitymatState_t state; + HANDLE_CUDM_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), + mode_extents.data(), 1, CUDA_C_64F, + &state)); + return state; } void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { - try { - auto status = - cudensitymatStateComputeScaling(handle, state, &scale_factor, stream); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to scale quantum state."); - } - } catch (const std::exception &e) { - std::cerr << "Error in scale_state: " << e.what() << std::endl; - throw; - } + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); } void destroy_state(cudensitymatState_t state) { - try { - cudensitymatDestroyState(state); - } catch (const std::exception &e) { - std::cerr << "Error in destroy_state: " << e.what() << std::endl; - } + cudensitymatDestroyState(state); } cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents) { - try { - if (c_ops.empty()) { - throw std::invalid_argument("Collapse operators cannot be empty."); - } - - cudensitymatOperator_t lindblad_op; - auto status = cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &lindblad_op); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create lindblad operator."); - } - - for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument( - "Collapse operator must be a square matrix."); - } + if (c_ops.empty()) { + throw std::invalid_argument("Collapse operators cannot be empty."); + } - std::vector> flat_matrix(dim * dim); - for (size_t i = 0; i < dim; i++) { - for (size_t j = 0; j < dim; j++) { - flat_matrix[i * dim + j] = c_op[{i, j}]; - } - } + cudensitymatOperator_t lindblad_op; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &lindblad_op)); - // Create Operator term for LtL and add to lindblad_op - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(lindblad_op); - throw std::runtime_error("Failed to create operator term."); - } + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument("Collapse operator must be a square matrix."); + } - // Attach terms and cleanup - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, - {1.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to append operator term."); + std::vector> flat_matrix(dim * dim); + for (size_t i = 0; i < dim; i++) { + for (size_t j = 0; j < dim; j++) { + flat_matrix[i * dim + j] = c_op[{i, j}]; } - - cudensitymatDestroyOperatorTerm(term); } - return lindblad_op; - } catch (const std::exception &e) { - std::cerr << "Error in compute_lindblad_op: " << e.what() << std::endl; - throw; + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term)); + cudensitymatDestroyOperator(lindblad_op); + + // Attach terms and cleanup + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, + 0, {1.0}, scalarCallback)); + cudensitymatDestroyOperatorTerm(term); } + + return lindblad_op; } cudensitymatOperator_t convert_to_cudensitymat_operator( diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 4ec8f100ba..ddceb989b9 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -75,7 +75,7 @@ TEST_F(CuDensityMatTestFixture, InitializeState) { std::vector mode_extents = {2, 2}; auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - 2, mode_extents); + mode_extents); ASSERT_NE(state, nullptr); @@ -88,7 +88,7 @@ TEST_F(CuDensityMatTestFixture, ScaleState) { ASSERT_NO_THROW({ auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - 2, mode_extents); + mode_extents); ASSERT_NE(state, nullptr); cudaStream_t stream; From e865f7683e29b839a0e64864eca0f79500f33de5 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 22 Jan 2025 08:59:06 -0800 Subject: [PATCH 138/311] Fixing convert_to_cudensitymat_operator as per the cudensitymat APIs Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 125 ++++++++++++------------ runtime/cudaq/operators.h | 5 +- 2 files changed, 68 insertions(+), 62 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 914a02914b..1fd7567f51 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -73,6 +73,15 @@ compute_lindblad_operator(cudensitymatHandle_t handle, return lindblad_op; } +std::map +convert_dimensions(const std::vector &mode_extents) { + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } + return dimensions; +} + cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, @@ -86,71 +95,67 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( throw std::runtime_error("Failed to create operator."); } - // Define dimensions for the operator - std::map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } - - auto matrix = op.to_matrix(dimensions, parameters); - size_t dim = matrix.get_rows(); - if (matrix.get_columns() != dim) { - throw std::invalid_argument("Matrix must be a square."); - } - - std::vector> flat_matrix; - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); + for (const auto &product_op : op.get_terms()) { + cudensitymatOperatorTerm_t term; + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term)); + + for (const auto &component : product_op.get_terms()) { + if (std::holds_alternative(component)) { + const auto &elem_op = std::get(component); + + // Create a cudensitymat elementary operator + cudensitymatElementaryOperator_t cudm_elem_op; + + // Get the matrix representation of elementary operator + auto dimensions = convert_dimensions(mode_extents); + auto matrix = elem_op.to_matrix(dimensions, parameters); + + // Flatten the matrix into a single-dimensional array + std::vector> flat_matrix; + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } + + // Create a cudensitymat elementary operator + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, 1, mode_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, + flat_matrix.data(), {nullptr, nullptr}, &cudm_elem_op)); + + // Append the elementary operator to the term + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 1, &cudm_elem_op, &elem_op.degrees[0], nullptr, + make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); + + // Destroy the elementary operator after appending + HANDLE_CUDM_ERROR( + cudensitymatDestroyElementaryOperator(cudm_elem_op)); + } else if (std::holds_alternative(component)) { + const auto &scalar_op = std::get(component); + + // Use the scalar coefficient + auto coeff = scalar_op.evaluate(parameters); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 0, nullptr, nullptr, nullptr, + {make_cuDoubleComplex(coeff.real(), coeff.imag())}, + {nullptr, nullptr})); + } } - } - - cudensitymatOperatorTerm_t term; - status = cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &term); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to create operator term."); - } - - // Attach flat_matrix to the term - int32_t num_elem_operators = 1; - int32_t num_operator_modes = static_cast(mode_extents.size()); - const int64_t *operator_mode_extents = mode_extents.data(); - const int64_t *operator_mode_strides = nullptr; - int32_t state_modes_acted_on[static_cast(mode_extents.size())]; - for (int32_t i = 0; i < num_operator_modes; i++) { - state_modes_acted_on[i] = i; - } - - cudensitymatWrappedTensorCallback_t tensorCallback = {nullptr, nullptr}; - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - void *tensor_data = flat_matrix.data(); - cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + // Append the product operator term to the top-level operator + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); - status = cudensitymatOperatorTermAppendGeneralProduct( - handle, term, num_elem_operators, &num_operator_modes, - &operator_mode_extents, &operator_mode_strides, state_modes_acted_on, - nullptr, CUDA_C_64F, &tensor_data, &tensorCallback, coefficient, - scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error( - "Failed to attach flat_matrix to operator term."); + // Destroy the term + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); } - status = cudensitymatOperatorAppendTerm(handle, operator_handle, term, 0, - coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyOperator(operator_handle); - throw std::runtime_error("Failed to attach term to operator."); - } - - cudensitymatDestroyOperatorTerm(term); return operator_handle; } catch (const std::exception &e) { std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 8924dbb861..fe088dbd79 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -125,7 +125,7 @@ class operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector get_terms() { return m_terms; } + std::vector get_terms() const { return m_terms; } }; operator_sum operator*(std::complex other, operator_sum self); operator_sum operator+(std::complex other, operator_sum self); @@ -218,7 +218,8 @@ class product_operator : public operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() { + std::vector> + get_terms() const { return m_terms; }; }; From 5668907c3f040bcb713116f1a69a41a0d4b98288 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 22 Jan 2025 11:36:52 -0800 Subject: [PATCH 139/311] Updating CMakeLists and unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 13 +++- unittests/CMakeLists.txt | 4 +- unittests/dynamics/test_cudm_helpers.cpp | 75 ++++++++++++------------ 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 1fd7567f51..dbd830e92c 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -14,14 +14,21 @@ cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, const std::vector &mode_extents) { cudensitymatState_t state; - HANDLE_CUDM_ERROR(cudensitymatCreateState(handle, purity, mode_extents.size(), - mode_extents.data(), 1, CUDA_C_64F, - &state)); + cudensitymatStatus_t status = + cudensitymatCreateState(handle, purity, mode_extents.size(), + mode_extents.data(), 1, CUDA_C_64F, &state); + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + std::cerr << "Error in cudensitymatCreateState: " << status << std::endl; + } return state; } void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { + if (!state) { + throw std::invalid_argument("Invalid state provided to scale_state."); + } + HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); } diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index bc1d391943..f87a25d1ec 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -293,7 +293,8 @@ target_link_libraries(test_operators cudaq gtest_main $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 - /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) + /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12 + fmt::fmt-header-only) gtest_discover_tests(test_operators) add_subdirectory(plugin) @@ -457,4 +458,3 @@ if (CUDAQ_ENABLE_PYTHON) gtest_discover_tests(test_domains TEST_SUFFIX _Sampling PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/python") endif() - diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index ddceb989b9..9b86448bc6 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -6,6 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include #include #include @@ -32,27 +33,41 @@ cudaq::operator_sum initialize_operator_sum() { class CuDensityMatTestFixture : public ::testing::Test { protected: cudensitymatHandle_t handle; + cudaStream_t stream; void SetUp() override { - auto status = cudensitymatCreate(&handle); - ASSERT_EQ(status, CUDENSITYMAT_STATUS_SUCCESS); + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + HANDLE_CUDA_ERROR(cudaStreamCreate(&stream)); } - void TearDown() override { cudensitymatDestroy(handle); } + void TearDown() override { + HANDLE_CUDA_ERROR(cudaStreamDestroy(stream)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle)); + } }; -// Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { +// Test for initialize_state +TEST_F(CuDensityMatTestFixture, InitializeState) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + mode_extents); + ASSERT_NE(state, nullptr); - auto result = - cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + cudaq::destroy_state(state); +} - ASSERT_NE(result, nullptr); +// Test for scale_state +TEST_F(CuDensityMatTestFixture, ScaleState) { + std::vector mode_extents = {2}; - cudensitymatDestroyOperator(result); + auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, + mode_extents); + ASSERT_NE(state, nullptr); + + EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + + cudaq::destroy_state(state); } // Test for compute_lindblad_op @@ -63,42 +78,24 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); std::vector c_ops = {c_op1, c_op2}; - auto result = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - - ASSERT_NE(result, nullptr); - - cudensitymatDestroyOperator(result); -} - -// Test for initialize_state -TEST_F(CuDensityMatTestFixture, InitializeState) { - std::vector mode_extents = {2, 2}; - - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - - ASSERT_NE(state, nullptr); + auto lindblad_op = + cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + ASSERT_NE(lindblad_op, nullptr); - cudaq::destroy_state(state); + cudensitymatDestroyOperator(lindblad_op); } -// Test for scale_state -TEST_F(CuDensityMatTestFixture, ScaleState) { +// Test for convert_to_cudensitymat_operator +TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - ASSERT_NO_THROW({ - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - ASSERT_NE(state, nullptr); - - cudaStream_t stream; - cudaStreamCreate(&stream); + auto op_sum = initialize_operator_sum(); - EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + auto result = + cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); - cudaStreamDestroy(stream); - cudaq::destroy_state(state); - }); + cudensitymatDestroyOperator(result); } // Test invalid handle From 5608fdde72cf9d4f8799ccfc06214da6794479c8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 23 Jan 2025 09:06:53 -0800 Subject: [PATCH 140/311] * Adding vector of cudensitymatElementaryOperator_t to store and destroy the cudensitymatElementaryOperator_t * Fetching subspace_extents using elementary_op degrees * Adding a vector with correct action duality Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 38 +++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index dbd830e92c..ea6972f715 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -102,6 +102,8 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( throw std::runtime_error("Failed to create operator."); } + std::vector elementary_operators; + for (const auto &product_op : op.get_terms()) { cudensitymatOperatorTerm_t term; @@ -113,6 +115,14 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( if (std::holds_alternative(component)) { const auto &elem_op = std::get(component); + std::vector subspace_extents; + for (int degree : elem_op.degrees) { + if (degree > mode_extents.size()) { + throw std::out_of_range("Degree exceeds mode_extents size."); + } + subspace_extents.push_back(mode_extents[degree]); + } + // Create a cudensitymat elementary operator cudensitymatElementaryOperator_t cudm_elem_op; @@ -130,23 +140,28 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( // Create a cudensitymat elementary operator HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, 1, mode_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, - flat_matrix.data(), {nullptr, nullptr}, &cudm_elem_op)); + handle, static_cast(subspace_extents.size()), + subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, + nullptr, CUDA_C_64F, flat_matrix.data(), {nullptr, nullptr}, + &cudm_elem_op)); + + elementary_operators.push_back(cudm_elem_op); + + // Default to left action + std::vector modeActionDuality(elem_op.degrees.size(), 0); // Append the elementary operator to the term HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 1, &cudm_elem_op, &elem_op.degrees[0], nullptr, - make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); - - // Destroy the elementary operator after appending - HANDLE_CUDM_ERROR( - cudensitymatDestroyElementaryOperator(cudm_elem_op)); + handle, term, 1, &cudm_elem_op, elem_op.degrees.data(), + modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); } else if (std::holds_alternative(component)) { const auto &scalar_op = std::get(component); // Use the scalar coefficient auto coeff = scalar_op.evaluate(parameters); + // TODO: Implement handling for time-dependent scalars using + // cudensitymatScalarCallback_t HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle, term, 0, nullptr, nullptr, nullptr, {make_cuDoubleComplex(coeff.real(), coeff.imag())}, @@ -161,6 +176,11 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( // Destroy the term HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + + // Cleanup + for (auto &elem_op : elementary_operators) { + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + } } return operator_handle; From 271558f69370b48fb977ab0495d2a25da4f03964 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 23 Jan 2025 09:42:11 -0800 Subject: [PATCH 141/311] Refactoring the code Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 157 ++++++++++++++---------- 1 file changed, 91 insertions(+), 66 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index ea6972f715..0993fa7f7d 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -10,6 +10,82 @@ #include "cudaq/cudm_error_handling.h" namespace cudaq { +// Function to flatten a matrix into a 1D array +std::vector> flatten_matrix(const matrix_2 &matrix) { + std::vector> flat_matrix; + + for (size_t i = 0; i < matrix.get_rows(); i++) { + for (size_t j = 0; j < matrix.get_columns(); j++) { + flat_matrix.push_back(matrix[{i, j}]); + } + } + + return flat_matrix; +} + +// Function to extract sub-space extents based on degrees +std::vector +get_subspace_extents(const std::vector &mode_extents, + const std::vector °rees) { + std::vector subspace_extents; + + for (int degree : degrees) { + if (degree >= mode_extents.size()) { + throw std::out_of_range("Degree exceeds mode_extents size."); + } + subspace_extents.push_back(mode_extents[degree]); + } + + return subspace_extents; +} + +// Function to create a cudensitymat elementary operator +cudensitymatElementaryOperator_t create_elementary_operator( + cudensitymatHandle_t handle, const std::vector &subspace_extents, + const std::vector> &flat_matrix) { + cudensitymatElementaryOperator_t cudm_elem_op; + + std::vector interleaved_matrix; + interleaved_matrix.reserve(flat_matrix.size() * 2); + + for (const auto &value : flat_matrix) { + interleaved_matrix.push_back(value.real()); + interleaved_matrix.push_back(value.imag()); + } + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, static_cast(subspace_extents.size()), + subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, + CUDA_C_64F, static_cast(interleaved_matrix.data()), + {nullptr, nullptr}, &cudm_elem_op)); + + return cudm_elem_op; +} + +// Function to append an elementary operator to a term +void append_elementary_operator_to_term( + cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, + cudensitymatElementaryOperator_t &elem_op, + const std::vector °rees) { + std::vector modeActionDuality(degrees.size(), 0); + + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, static_cast(degrees.size()), &elem_op, + degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); +} + +// Function to create and append a scalar to a term +void append_scalar_to_term(cudensitymatHandle_t handle, + cudensitymatOperatorTerm_t term, + const std::complex &coeff) { + // TODO: Implement handling for time-dependent scalars using + // cudensitymatScalarCallback_t + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 0, nullptr, nullptr, nullptr, + {make_cuDoubleComplex(coeff.real(), coeff.imag())}, {nullptr, nullptr})); +} + cudensitymatState_t initialize_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, const std::vector &mode_extents) { @@ -51,26 +127,15 @@ compute_lindblad_operator(cudensitymatHandle_t handle, &lindblad_op)); for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument("Collapse operator must be a square matrix."); - } + auto flat_matrix = flatten_matrix(c_op); - std::vector> flat_matrix(dim * dim); - for (size_t i = 0; i < dim; i++) { - for (size_t j = 0; j < dim; j++) { - flat_matrix[i * dim + j] = c_op[{i, j}]; - } - } - - // Create Operator term for LtL and add to lindblad_op cudensitymatOperatorTerm_t term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); cudensitymatDestroyOperator(lindblad_op); - // Attach terms and cleanup + // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback)); @@ -95,12 +160,9 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( const operator_sum &op, const std::vector &mode_extents) { try { cudensitymatOperator_t operator_handle; - auto status = cudensitymatCreateOperator( + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle, static_cast(mode_extents.size()), mode_extents.data(), - &operator_handle); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create operator."); - } + &operator_handle)); std::vector elementary_operators; @@ -115,57 +177,20 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( if (std::holds_alternative(component)) { const auto &elem_op = std::get(component); - std::vector subspace_extents; - for (int degree : elem_op.degrees) { - if (degree > mode_extents.size()) { - throw std::out_of_range("Degree exceeds mode_extents size."); - } - subspace_extents.push_back(mode_extents[degree]); - } - - // Create a cudensitymat elementary operator - cudensitymatElementaryOperator_t cudm_elem_op; - - // Get the matrix representation of elementary operator - auto dimensions = convert_dimensions(mode_extents); - auto matrix = elem_op.to_matrix(dimensions, parameters); - - // Flatten the matrix into a single-dimensional array - std::vector> flat_matrix; - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); - } - } - - // Create a cudensitymat elementary operator - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, static_cast(subspace_extents.size()), - subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, - nullptr, CUDA_C_64F, flat_matrix.data(), {nullptr, nullptr}, - &cudm_elem_op)); + auto subspace_extents = + get_subspace_extents(mode_extents, elem_op.degrees); + auto flat_matrix = flatten_matrix( + elem_op.to_matrix(convert_dimensions(mode_extents), parameters)); + auto cudm_elem_op = + create_elementary_operator(handle, subspace_extents, flat_matrix); elementary_operators.push_back(cudm_elem_op); - - // Default to left action - std::vector modeActionDuality(elem_op.degrees.size(), 0); - - // Append the elementary operator to the term - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 1, &cudm_elem_op, elem_op.degrees.data(), - modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), - {nullptr, nullptr})); + append_elementary_operator_to_term(handle, term, cudm_elem_op, + elem_op.degrees); } else if (std::holds_alternative(component)) { - const auto &scalar_op = std::get(component); - - // Use the scalar coefficient - auto coeff = scalar_op.evaluate(parameters); - // TODO: Implement handling for time-dependent scalars using - // cudensitymatScalarCallback_t - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 0, nullptr, nullptr, nullptr, - {make_cuDoubleComplex(coeff.real(), coeff.imag())}, - {nullptr, nullptr})); + auto coeff = + std::get(component).evaluate(parameters); + append_scalar_to_term(handle, term, coeff); } } From a4c104aaeaf45935c61c1f3cc4e699a6ef9a7117 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 23 Jan 2025 15:41:54 -0800 Subject: [PATCH 142/311] Fixing the matrix in the unittest and other code in compute_lindblad apart for temp vector Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 21 +++++++++++++++++++-- runtime/cudaq/utils/tensor.cpp | 1 + unittests/dynamics/test_cudm_helpers.cpp | 4 ++-- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 0993fa7f7d..999ba90ff5 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -127,19 +127,36 @@ compute_lindblad_operator(cudensitymatHandle_t handle, &lindblad_op)); for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument("Collapse operator must be a square matrix"); + } + auto flat_matrix = flatten_matrix(c_op); + // Create Operator term for LtL and add to lindblad_op cudensitymatOperatorTerm_t term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); - cudensitymatDestroyOperator(lindblad_op); + + // Create elementary operator form c_op + auto cudm_elem_op = + create_elementary_operator(handle, mode_extents, flat_matrix); + + // Add the elementary operator to the term + // TODO: Fix temp vector below + std::vector temp; + append_elementary_operator_to_term(handle, term, cudm_elem_op, temp); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, 0, {1.0}, scalarCallback)); - cudensitymatDestroyOperatorTerm(term); + + // Destroy intermediate resources + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(cudm_elem_op)); } return lindblad_op; diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index f37f959090..6d4aaa9764 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/utils/tensor.h" +#include #include inline std::complex &access(std::complex *p, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 9b86448bc6..734470365a 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -74,8 +74,8 @@ TEST_F(CuDensityMatTestFixture, ScaleState) { TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { std::vector mode_extents = {2, 2}; - cudaq::matrix_2 c_op1({{1.0, 0.0}, {0.0, 0.0}}, {2, 2}); - cudaq::matrix_2 c_op2({{0.0, 0.0}, {0.0, 1.0}}, {2, 2}); + cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); + cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); std::vector c_ops = {c_op1, c_op2}; auto lindblad_op = From 11c7b65583fef09cf6a5e92fd539e24cbd79fac0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 24 Jan 2025 21:39:13 -0800 Subject: [PATCH 143/311] Adding cudm_state with unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 76 +++++++++++ runtime/cudaq/dynamics/CMakeLists.txt | 2 +- runtime/cudaq/dynamics/cudm_state.cpp | 173 +++++++++++++++++++++++++ runtime/cudaq/dynamics/cudm_state.h | 28 ---- unittests/CMakeLists.txt | 2 + unittests/dynamics/test_cudm_state.cpp | 117 +++++++++++++++++ 6 files changed, 369 insertions(+), 29 deletions(-) create mode 100644 runtime/cudaq/cudm_state.h create mode 100644 runtime/cudaq/dynamics/cudm_state.cpp delete mode 100644 runtime/cudaq/dynamics/cudm_state.h create mode 100644 unittests/dynamics/test_cudm_state.cpp diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h new file mode 100644 index 0000000000..814a7ad342 --- /dev/null +++ b/runtime/cudaq/cudm_state.h @@ -0,0 +1,76 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace cudaq { +class cudm_mat_state { +public: + /// @brief To initialize state with raw data. + explicit cudm_mat_state(std::vector> rawData); + + /// @brief Destructor to clean up resources + ~cudm_mat_state(); + + /// @brief Initialize the state as a density matrix or state vector based on + /// dimensions. + /// @param hilbertSpaceDims Vector representing the Hilbert Space dimensions. + void init_state(const std::vector &hilbertSpaceDims); + + /// @brief Check if the state is initialized. + /// @return True if the state is initialized, false otherwise. + bool is_initialized() const; + + /// @brief Check if the state is a density matrix. + /// @return True if the state is a density matrix, false otherwise. + bool is_density_matrix() const; + + /// @brief Dump the state data to a string for debugging purposes. + /// @return String representation of the state data. + std::string dump() const; + + /// @brief Convert the state vector to a density matrix. + /// @return A new cudm_mat_state representing the density matrix. + cudm_mat_state to_density_matrix() const; + + /// @brief Get the underlying implementation (if any). + /// @return The underlying state implementation. + cudensitymatState_t get_impl() const; + + /// @brief Attach raw data to the internal state representation + void attach_storage(); + +private: + std::vector> rawData_; + cudensitymatState_t state_; + cudensitymatHandle_t handle_; + std::vector hilbertSpaceDims_; + + /// @brief Calculate the size of the state vector for the given Hilbert space + /// dimensions. + /// @param hilbertSpaceDims Hilbert space dimensions. + /// @return Size of the state vector. + size_t calculate_state_vector_size( + const std::vector &hilbertSpaceDims) const; + + /// @brief Calculate the size of the density matrix for the given Hilbert + /// space dimensions. + /// @param hilbertSpaceDims Hilbert space dimensions. + /// @return Size of the density matrix. + size_t calculate_density_matrix_size( + const std::vector &hilbertSpaceDims) const; +}; + +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 283e77e7ff..90354c7e96 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp new file mode 100644 index 0000000000..dfbac25057 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include + +namespace cudaq { + +cudm_mat_state::cudm_mat_state(std::vector> rawData) + : rawData_(rawData), state_(nullptr), handle_(nullptr), + hilbertSpaceDims_() { + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); +} + +cudm_mat_state::~cudm_mat_state() { + if (state_) { + cudensitymatDestroyState(state_); + } + if (handle_) { + cudensitymatDestroy(handle_); + } +} + +void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { + if (state_) { + throw std::runtime_error("State is already initialized."); + } + + hilbertSpaceDims_ = hilbertSpaceDims; + cudensitymatStatePurity_t purity; + + if (rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims)) { + purity = CUDENSITYMAT_STATE_PURITY_MIXED; + } else if (rawData_.size() == calculate_state_vector_size(hilbertSpaceDims)) { + purity = CUDENSITYMAT_STATE_PURITY_PURE; + } else { + throw std::invalid_argument( + "Invalid rawData size for the given Hilbert space dimensions."); + } + + HANDLE_CUDM_ERROR(cudensitymatCreateState( + handle_, purity, static_cast(hilbertSpaceDims.size()), + hilbertSpaceDims.data(), 1, CUDA_C_64F, &state_)); + + attach_storage(); +} + +bool cudm_mat_state::is_initialized() const { return state_ != nullptr; } + +bool cudm_mat_state::is_density_matrix() const { + if (!is_initialized()) { + return false; + } + + return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); +} + +std::string cudm_mat_state::dump() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + std::ostringstream oss; + oss << "State data: ["; + for (size_t i = 0; i < rawData_.size(); i++) { + oss << rawData_[i]; + if (i < rawData_.size() - 1) { + oss << ", "; + } + } + oss << "]"; + return oss.str(); +} + +cudm_mat_state cudm_mat_state::to_density_matrix() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + if (is_density_matrix()) { + throw std::runtime_error("State is already a density matrix."); + } + + std::vector> densityMatrix; + size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims_); + size_t expectedDensityMatrixSize = vectorSize * vectorSize; + densityMatrix.resize(expectedDensityMatrixSize); + + for (size_t i = 0; i < vectorSize; i++) { + for (size_t j = 0; j < vectorSize; j++) { + densityMatrix[i * vectorSize + j] = rawData_[i] * std::conj(rawData_[j]); + } + } + + cudm_mat_state densityMatrixState(densityMatrix); + densityMatrixState.init_state(hilbertSpaceDims_); + return densityMatrixState; +} + +cudensitymatState_t cudm_mat_state::get_impl() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + return state_; +} + +void cudm_mat_state::attach_storage() { + if (!state_) { + throw std::runtime_error("State is not initialized."); + } + + if (rawData_.empty()) { + throw std::runtime_error("Raw data is empty. Cannot attach storage."); + } + + // Retrieve the number of state components + int32_t numStateComponents; + HANDLE_CUDM_ERROR( + cudensitymatStateGetNumComponents(handle_, state_, &numStateComponents)); + + // Retrieve the storage size for each component + std::vector componentBufferSizes(numStateComponents); + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + handle_, state_, numStateComponents, componentBufferSizes.data())); + + // Validate that rawData_ has sufficient space for all components + size_t totalSize = 0; + for (size_t size : componentBufferSizes) { + totalSize += size; + } + if (rawData_.size() * sizeof(std::complex) < totalSize) { + throw std::invalid_argument( + "Raw data size is insufficient to cover all components."); + } + + // Attach storage for each component + std::vector componentBuffers(numStateComponents); + std::vector *> rawComponentData(numStateComponents); + + size_t offset = 0; + for (int32_t i = 0; i < numStateComponents; i++) { + rawComponentData[i] = + reinterpret_cast *>(rawData_.data()) + offset; + componentBuffers[i] = static_cast(rawComponentData[i]); + offset += componentBufferSizes[i] / sizeof(std::complex); + } + + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + handle_, state_, numStateComponents, componentBuffers.data(), + componentBufferSizes.data())); +} + +size_t cudm_mat_state::calculate_state_vector_size( + const std::vector &hilbertSpaceDims) const { + size_t size = 1; + for (auto dim : hilbertSpaceDims) { + size *= dim; + } + return size; +} + +size_t cudm_mat_state::calculate_density_matrix_size( + const std::vector &hilbertSpaceDims) const { + size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); + return vectorSize * vectorSize; +} +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_state.h b/runtime/cudaq/dynamics/cudm_state.h deleted file mode 100644 index 1f20f9483f..0000000000 --- a/runtime/cudaq/dynamics/cudm_state.h +++ /dev/null @@ -1,28 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include -#include - -namespace cudaq { -class cudm_mat_state { -public: - cudm_mat_state(cudensitymatHandle_t handle, cudensitymatStatePurity_t purity, - int num_modes, const std::vector &mode_extents); - ~cudm_mat_state(); - - void scale(double factor, cudaStream_t stream); - cudensitymatState_t get() const; - -private: - cudensitymatState_t state; - cudensitymatHandle_t handle; -}; -} // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f87a25d1ec..430fac8ca3 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -54,6 +54,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp dynamics/test_cudm_helpers.cpp + dynamics/test_cudm_state.cpp ) # Make it so we can get function symbols @@ -281,6 +282,7 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp dynamics/test_cudm_helpers.cpp + dynamics/test_cudm_state.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp new file mode 100644 index 0000000000..237142fcd0 --- /dev/null +++ b/unittests/dynamics/test_cudm_state.cpp @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +using namespace cudaq; + +class CuDensityMatStateTest : public ::testing::Test { +protected: + void SetUp() override { + // Set up test data for a single 2-qubit system + hilbertSpaceDims = {2, 2}; + + // State vector (pure state) for |00> + stateVectorData = { + std::complex(1.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0)}; + + // Density matrix for |00><00| + densityMatrixData = { + std::complex(1.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0), + std::complex(0.0, 0.0), std::complex(0.0, 0.0)}; + } + + void TearDown() override {} + + std::vector hilbertSpaceDims; + std::vector> stateVectorData; + std::vector> densityMatrixData; +}; + +TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { + cudm_mat_state state(stateVectorData); + EXPECT_FALSE(state.is_initialized()); + + EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); + EXPECT_TRUE(state.is_initialized()); + EXPECT_FALSE(state.is_density_matrix()); + + EXPECT_NO_THROW(state.dump()); +} + +TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { + cudm_mat_state state(densityMatrixData); + EXPECT_FALSE(state.is_initialized()); + + EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); + EXPECT_TRUE(state.is_initialized()); + EXPECT_TRUE(state.is_density_matrix()); + + EXPECT_NO_THROW(state.dump()); +} + +TEST_F(CuDensityMatStateTest, InvalidInitialization) { + // Data size mismatch for hilbertSpaceDims (2x2 system expects size 4 or 16) + std::vector> invalidData = { + std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; + + cudm_mat_state state(invalidData); + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); +} + +TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { + cudm_mat_state state(stateVectorData); + state.init_state(hilbertSpaceDims); + + EXPECT_FALSE(state.is_density_matrix()); + + cudm_mat_state densityMatrixState = state.to_density_matrix(); + + EXPECT_TRUE(densityMatrixState.is_density_matrix()); + EXPECT_TRUE(densityMatrixState.is_initialized()); + + EXPECT_NO_THROW(densityMatrixState.dump()); +} + +TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { + cudm_mat_state state(densityMatrixData); + state.init_state(hilbertSpaceDims); + + EXPECT_TRUE(state.is_density_matrix()); + EXPECT_THROW(state.to_density_matrix(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, DumpUninitializedState) { + cudm_mat_state state(stateVectorData); + EXPECT_THROW(state.dump(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.attach_storage(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, DestructorCleansUp) { + cudm_mat_state state(stateVectorData); + + EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); +} From 40e52d910061dbf98fa8767ec2c179600a49e4c2 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 25 Jan 2025 11:57:24 -0800 Subject: [PATCH 144/311] Addign few more unit tests for cudm_state Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_state.cpp | 20 ++++++-- unittests/dynamics/test_cudm_state.cpp | 68 ++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index dfbac25057..b456bef45d 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -33,15 +33,25 @@ void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { } hilbertSpaceDims_ = hilbertSpaceDims; + + size_t rawDataSize = rawData_.size(); + size_t expectedDensityMatrixSize = + calculate_density_matrix_size(hilbertSpaceDims); + size_t expectedStateVectorSize = + calculate_state_vector_size(hilbertSpaceDims); + + if (rawDataSize != expectedDensityMatrixSize && + rawDataSize != expectedStateVectorSize) { + throw std::invalid_argument( + "Invalid rawData size for the given Hilbert space dimensions."); + } + cudensitymatStatePurity_t purity; - if (rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims)) { + if (rawDataSize == expectedDensityMatrixSize) { purity = CUDENSITYMAT_STATE_PURITY_MIXED; - } else if (rawData_.size() == calculate_state_vector_size(hilbertSpaceDims)) { + } else if (rawDataSize == expectedStateVectorSize) { purity = CUDENSITYMAT_STATE_PURITY_PURE; - } else { - throw std::invalid_argument( - "Invalid rawData size for the given Hilbert space dimensions."); } HANDLE_CUDM_ERROR(cudensitymatCreateState( diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 237142fcd0..d8ef07ffc4 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -115,3 +115,71 @@ TEST_F(CuDensityMatStateTest, DestructorCleansUp) { EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); } + +TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { + std::vector> emptyData; + cudm_mat_state state(emptyData); + + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); +} + +TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { + hilbertSpaceDims = {2}; + stateVectorData = {std::complex(1.0, 0.0), + std::complex(0.0, 0.0)}; + cudm_mat_state state(stateVectorData); + + state.init_state(hilbertSpaceDims); + + EXPECT_FALSE(state.is_density_matrix()); + + cudm_mat_state densityMatrixState = state.to_density_matrix(); + EXPECT_TRUE(densityMatrixState.is_density_matrix()); + EXPECT_TRUE(densityMatrixState.is_initialized()); + + EXPECT_NO_THROW(densityMatrixState.dump()); +} + +TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { + // 3x3 space is not supported by the provided rawData size + hilbertSpaceDims = {3, 3}; + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); +} + +TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.to_density_matrix(), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, MultipleInitialization) { + cudm_mat_state state(stateVectorData); + state.init_state(hilbertSpaceDims); + + EXPECT_TRUE(state.is_initialized()); + + EXPECT_THROW(state.init_state(hilbertSpaceDims), std::runtime_error); +} + +TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { + cudm_mat_state state(densityMatrixData); + state.init_state(hilbertSpaceDims); + + EXPECT_TRUE(state.is_density_matrix()); + EXPECT_TRUE(state.is_initialized()); +} + +TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { + cudm_mat_state state(stateVectorData); + state.init_state(hilbertSpaceDims); + + EXPECT_NO_THROW(state.dump()); +} + +TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { + cudm_mat_state state(stateVectorData); + + EXPECT_THROW(state.dump(), std::runtime_error); +} From 4ea641543899afb754148451a5f9fc6748522e24 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 25 Jan 2025 15:04:22 -0800 Subject: [PATCH 145/311] Adding an enum with initial quantum state and initilize_state based on InitialStateArgT Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 25 +++++++++++++++++++++++++ runtime/cudaq/helpers.h | 11 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index e6c50d14a0..0c03a81d97 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/helpers.h" +#include #include #include @@ -144,4 +145,28 @@ OperatorHelpers::canonicalize_degrees(const std::vector °rees) { std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); return sorted_degrees; } + +// Initialize state based on InitialStateArgT +// TODO: Implement quantum state initialization +void OperatorHelpers::initialize_state(const InitialStateArgT &stateArg, + const std::vector &dimensions) { + if (std::holds_alternative(stateArg)) { + InitialState initState = std::get(stateArg); + switch (initState) { + case InitialState::ZERO: + // ZERO quantum state initialization + std::cout << "Initialized to ZERO state." << std::endl; + break; + case InitialState::UNIFORM: + // UNIFORM quantum state initialization + std::cout << "Initialized to UNIFORM state." << std::endl; + break; + } + } else if (std::holds_alternative(stateArg)) { + // Initialization from runtime state + std::cout << "Initialized using runtime state." << std::endl; + } else { + throw std::invalid_argument("Unsupported InitialStateArgT type."); + } +} } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 52bb131d0c..39f73f7969 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -15,9 +15,16 @@ #include #include #include +#include #include namespace cudaq { + +// Enum to specify the initial quantum state. +enum class InitialState { ZERO, UNIFORM }; + +using InitialStateArgT = std::variant; + class OperatorHelpers { public: // Aggregate parameters from multiple mappings. @@ -46,5 +53,9 @@ class OperatorHelpers { // Canonicalize degrees by sorting in descending order. static std::vector canonicalize_degrees(const std::vector °rees); + + // Initialize state based on InitialStateArgT. + static void initialize_state(const InitialStateArgT &stateArg, + const std::vector &dimensions); }; } // namespace cudaq \ No newline at end of file From 31709a4cdeb1cc239fe13c883d50e06c93ab2759 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 25 Jan 2025 16:03:10 -0800 Subject: [PATCH 146/311] Fixing spelling Signed-off-by: Sachin Pisal --- runtime/cudaq/evolution.h | 4 ++-- runtime/cudaq/operators.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 7b5f717806..85379f998a 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -53,7 +53,7 @@ class Evolution { const std::vector>> &schedule_parameters); - /// Evolves a single quantum state under a given hamiltonian. + /// Evolves a single quantum state under a given `hamiltonian`. static evolve_result evolve_single(const operator_sum &hamiltonian, const std::map &dimensions, @@ -64,7 +64,7 @@ class Evolution { std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); - /// Evolves a single or multiple quantum states under a given hamiltonian. + /// Evolves a single or multiple quantum states under a given `hamiltonian`. /// Run only for dynamics target else throw error static std::vector evolve(const operator_sum &hamiltonian, const std::map &dimensions, diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index fe088dbd79..5f07f5dd49 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -473,12 +473,12 @@ class rydberg_hamiltonian : public operator_sum { /// @brief Constructor. /// @param atom_sites List of 2D coordinates for trap sites. - /// @param amplitude Time-dependant driving amplitude, Omega(t). - /// @param phase Time-dependant driving phase, phi(t). - /// @param delta_global Time-dependant driving detuning, Delta_global(t). + /// @param amplitude Time-dependent driving amplitude, Omega(t). + /// @param phase Time-dependent driving phase, phi(t). + /// @param delta_global Time-dependent driving detuning, Delta_global(t). /// @param atom_filling Optional. Marks occupied trap sites (1) and empty /// sites (0). Defaults to all sites occupied. - /// @param delta_local Optional. A tuple of Delta_local(t) and site dependant + /// @param delta_local Optional. A tuple of Delta_local(t) and site dependent /// local detuning factors. rydberg_hamiltonian( const std::vector &atom_sites, From dafc31a453aac61e7cb53e3f4ae9adca1edd46d8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 27 Jan 2025 16:09:57 -0800 Subject: [PATCH 147/311] * Renaming cudm_mat_state -> cudm_state * Adding create_initial_state method to create initial state based on the passed InitialStateArgT Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 25 ++++++-- runtime/cudaq/dynamics/cudm_state.cpp | 85 +++++++++++++++++++++++---- 2 files changed, 93 insertions(+), 17 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 814a7ad342..e7f858c8b7 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -16,13 +16,28 @@ #include namespace cudaq { -class cudm_mat_state { +// Enum to specify the initial quantum state. +enum class InitialState { ZERO, UNIFORM }; + +using InitialStateArgT = std::variant; + +class cudm_state { public: /// @brief To initialize state with raw data. - explicit cudm_mat_state(std::vector> rawData); + explicit cudm_state(std::vector> rawData); /// @brief Destructor to clean up resources - ~cudm_mat_state(); + ~cudm_state(); + + /// @brief Factory method to create an initial state. + /// @param InitialStateArgT The type or representation of the initial state. + /// @param Dimensions of the Hilbert space. + /// @param hasCollapseOps Whether collapse operators are present. + /// @return A new 'cudm_state' initialized to the specified state. + static cudm_state + create_initial_state(const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, + bool hasCollapseOps); /// @brief Initialize the state as a density matrix or state vector based on /// dimensions. @@ -42,8 +57,8 @@ class cudm_mat_state { std::string dump() const; /// @brief Convert the state vector to a density matrix. - /// @return A new cudm_mat_state representing the density matrix. - cudm_mat_state to_density_matrix() const; + /// @return A new cudm_state representing the density matrix. + cudm_state to_density_matrix() const; /// @brief Get the underlying implementation (if any). /// @return The underlying state implementation. diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index b456bef45d..40d6d49796 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -6,19 +6,22 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include #include #include +#include #include +#include namespace cudaq { -cudm_mat_state::cudm_mat_state(std::vector> rawData) +cudm_state::cudm_state(std::vector> rawData) : rawData_(rawData), state_(nullptr), handle_(nullptr), hilbertSpaceDims_() { HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); } -cudm_mat_state::~cudm_mat_state() { +cudm_state::~cudm_state() { if (state_) { cudensitymatDestroyState(state_); } @@ -27,7 +30,7 @@ cudm_mat_state::~cudm_mat_state() { } } -void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { +void cudm_state::init_state(const std::vector &hilbertSpaceDims) { if (state_) { throw std::runtime_error("State is already initialized."); } @@ -61,9 +64,9 @@ void cudm_mat_state::init_state(const std::vector &hilbertSpaceDims) { attach_storage(); } -bool cudm_mat_state::is_initialized() const { return state_ != nullptr; } +bool cudm_state::is_initialized() const { return state_ != nullptr; } -bool cudm_mat_state::is_density_matrix() const { +bool cudm_state::is_density_matrix() const { if (!is_initialized()) { return false; } @@ -71,7 +74,7 @@ bool cudm_mat_state::is_density_matrix() const { return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); } -std::string cudm_mat_state::dump() const { +std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); } @@ -88,7 +91,7 @@ std::string cudm_mat_state::dump() const { return oss.str(); } -cudm_mat_state cudm_mat_state::to_density_matrix() const { +cudm_state cudm_state::to_density_matrix() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); } @@ -108,19 +111,19 @@ cudm_mat_state cudm_mat_state::to_density_matrix() const { } } - cudm_mat_state densityMatrixState(densityMatrix); + cudm_state densityMatrixState(densityMatrix); densityMatrixState.init_state(hilbertSpaceDims_); return densityMatrixState; } -cudensitymatState_t cudm_mat_state::get_impl() const { +cudensitymatState_t cudm_state::get_impl() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); } return state_; } -void cudm_mat_state::attach_storage() { +void cudm_state::attach_storage() { if (!state_) { throw std::runtime_error("State is not initialized."); } @@ -166,7 +169,7 @@ void cudm_mat_state::attach_storage() { componentBufferSizes.data())); } -size_t cudm_mat_state::calculate_state_vector_size( +size_t cudm_state::calculate_state_vector_size( const std::vector &hilbertSpaceDims) const { size_t size = 1; for (auto dim : hilbertSpaceDims) { @@ -175,9 +178,67 @@ size_t cudm_mat_state::calculate_state_vector_size( return size; } -size_t cudm_mat_state::calculate_density_matrix_size( +size_t cudm_state::calculate_density_matrix_size( const std::vector &hilbertSpaceDims) const { size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); return vectorSize * vectorSize; } + +// Initialize state based on InitialStateArgT +cudm_state +cudm_state::create_initial_state(const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, + bool hasCollapseOps) { + size_t stateVectorSize = + std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), + static_cast(1), std::multiplies<>{}); + + std::vector> rawData; + + if (std::holds_alternative(initialStateArg)) { + InitialState initialState = std::get(initialStateArg); + + if (initialState == InitialState::ZERO) { + rawData.resize(stateVectorSize, {0.0, 0.0}); + // |0> state + rawData[0] = {1.0, 0.0}; + } else if (initialState == InitialState::UNIFORM) { + rawData.resize(stateVectorSize, {1.0 / std::sqrt(stateVectorSize), 0.0}); + } else { + throw std::invalid_argument("Unsupported InitialState type."); + } + } else if (std::holds_alternative(initialStateArg)) { + void *runtimeState = std::get(initialStateArg); + if (!runtimeState) { + throw std::invalid_argument("Runtime state pointer is null."); + } + + try { + auto *externalData = + reinterpret_cast> *>(runtimeState); + + if (!externalData || externalData->empty()) { + throw std::invalid_argument( + "Runtime state contains invalid or empty data."); + } + + rawData = *externalData; + } catch (const std::exception &e) { + throw std::runtime_error("Failed to interpret runtime state: " + + std::string(e.what())); + } + } else { + throw std::invalid_argument("Unsupported InitialStateArgT type."); + } + + cudm_state state(rawData); + state.init_state(hilbertSpaceDims); + + // Convert to a density matrix if collapse operators are present. + if (hasCollapseOps && !state.is_density_matrix()) { + state = state.to_density_matrix(); + } + + return state; +} } // namespace cudaq From e51696ab7a0bc059ac94a8ab3213475986de1a32 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 27 Jan 2025 17:07:25 -0800 Subject: [PATCH 148/311] Exposing state's rawData to be used in the stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 4 ++++ runtime/cudaq/dynamics/cudm_state.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index e7f858c8b7..0425e6db7c 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -67,6 +67,10 @@ class cudm_state { /// @brief Attach raw data to the internal state representation void attach_storage(); + /// @brief Get a copy of the raw data representing the quantum state. + /// @return A copy of the raw data as a vector of complex numbers. + std::vector> get_raw_data() const; + private: std::vector> rawData_; cudensitymatState_t state_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 40d6d49796..11b643d177 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -74,6 +74,10 @@ bool cudm_state::is_density_matrix() const { return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); } +std::vector> cudm_state::get_raw_data() const { + return rawData_; +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From 8a0bd3a126580d200c5e410831fed1c10652b42a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 28 Jan 2025 15:02:31 -0800 Subject: [PATCH 149/311] Adding cudm_time_stepper with unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_solver.h | 52 +++++++++++ runtime/cudaq/cudm_state.h | 4 + runtime/cudaq/cudm_time_stepper.h | 26 ++++++ runtime/cudaq/dynamics/CMakeLists.txt | 2 +- runtime/cudaq/dynamics/cudm_solver.cpp | 52 +++++++++++ runtime/cudaq/dynamics/cudm_state.cpp | 4 + runtime/cudaq/dynamics/cudm_time_stepper.cpp | 57 ++++++++++++ runtime/cudaq/dynamics/helpers.cpp | 25 +----- runtime/cudaq/helpers.h | 12 +-- unittests/CMakeLists.txt | 3 +- unittests/dynamics/test_cudm_state.cpp | 36 ++++---- unittests/dynamics/test_cudm_time_stepper.cpp | 86 +++++++++++++++++++ 12 files changed, 303 insertions(+), 56 deletions(-) create mode 100644 runtime/cudaq/cudm_solver.h create mode 100644 runtime/cudaq/cudm_time_stepper.h create mode 100644 runtime/cudaq/dynamics/cudm_solver.cpp create mode 100644 runtime/cudaq/dynamics/cudm_time_stepper.cpp create mode 100644 unittests/dynamics/test_cudm_time_stepper.cpp diff --git a/runtime/cudaq/cudm_solver.h b/runtime/cudaq/cudm_solver.h new file mode 100644 index 0000000000..2c7e14bfca --- /dev/null +++ b/runtime/cudaq/cudm_solver.h @@ -0,0 +1,52 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include "runtime/common/EvolveResult.h" +#include +#include +#include + +namespace cudaq { + +// Configuration struct for the solver +struct Config { + std::map dimensions; // Hilbert space dimensions + operator_sum hamiltonian; // Hamiltonian operator + std::vector collapse_operators; // Collapse operators + std::vector observables; // Observables to evaluate + std::variant>> initial_state; // Initial state + Schedule schedule; // Evolution schedule + bool store_intermediate_results = false; // Flag to store intermediate states +}; + +class cudm_solver { +public: + cudm_solver(const Config &config); + + void validate_config(); + + cudm_state initialize_state(); + + void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &obervable_ops, evolve_result &result); + + evolve_result evolve_dynamics(); + + cudensitymatOperator_t construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve); + +private: + Config config_; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 0425e6db7c..ad7eb22cae 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -71,6 +71,10 @@ class cudm_state { /// @return A copy of the raw data as a vector of complex numbers. std::vector> get_raw_data() const; + /// @brief Get a copy of the hilbert space dimensions for the quantum state. + /// @return A copy of the hilbert space dimensions of a vector of integers. + std::vector get_hilbert_space_dims() const; + private: std::vector> rawData_; cudensitymatState_t state_; diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/cudaq/cudm_time_stepper.h new file mode 100644 index 0000000000..59b0d942d8 --- /dev/null +++ b/runtime/cudaq/cudm_time_stepper.h @@ -0,0 +1,26 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/base_time_stepper.h" +#include "cudaq/cudm_state.h" +#include + +namespace cudaq { +class cudm_time_stepper : public BaseTimeStepper { +public: + explicit cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian); + + void compute(cudm_state &state, double t, double step_size) override; + +private: + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; +}; +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 90354c7e96..636d8b4096 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp + scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp cudm_time_stepper.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/cudaq/dynamics/cudm_solver.cpp new file mode 100644 index 0000000000..4f76d4afc8 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_solver.cpp @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_solver.h" +#include "cudaq/cudm_helpers.h" +#include "cudaq/cudm_state.h" +#include "cudaq/base_time_stepper.h" + +namespace cudaq { +cudm_solver::cudm_solver(const Config &config) : config_(config) { + validate_config(); +} + +void cudm_solver::validate_config() { + if (config_.dimensions.empty()) { + throw std::invalid_argument("Dimensions map cannot be empty."); + } + + if (config_.hamiltonian.get_terms().empty()) { + throw std::invalid_argument("Hamiltonian must have at least one term."); + } + + if (config_.dimensions.empty()) { + throw std::invalid_argument("Schedule cannot be empty."); + } +} + +cudm_state cudm_solver::initialize_state() { + std::vector mode_extents; + for (const auto &[key, value] : config_.dimensions) { + mode_extents.push_back(value); + } + + return cudm_state::create_initial_state(config_.initial_state, mode_extents, !config_.collapse_operators.empty()); +} + +cudensitymatOperator_t cudm_solver::construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve) { + return construct_liovillian(handle, hamiltonian, collapse_operators, me_solve ? 1.0 : 0.0); +} + +void cudm_solver::evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &observable_ops, evolve_result &result) { + auto handle = state.get_impl(); + + // Initialize the stepper + BaseTimeStepper timeStepper(liouvillian, handle); +} +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 11b643d177..1e84683b0e 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -78,6 +78,10 @@ std::vector> cudm_state::get_raw_data() const { return rawData_; } +std::vector cudm_state::get_hilbert_space_dims() const { + return hilbertSpaceDims_; +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp new file mode 100644 index 0000000000..86de154442 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_time_stepper.h" +#include "cudaq/cudm_error_handling.h" +#include + +namespace cudaq { +cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) : handle_(handle), liouvillian_(liouvillian) {} + +void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { + if (!state.is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + std::cout << "Preparing workspace ..." << std::endl; + // Prepare workspace + cudensitymatWorkspaceDescriptor_t workspace; + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); + if (!workspace) { + throw std::runtime_error("Failed to create workspace for the operator."); + } + + std::cout << "Create a new state for the next step ..." << std::endl; + // Create a new state for the next step + cudm_state next_state(state.to_density_matrix().get_raw_data()); + next_state.init_state(state.get_hilbert_space_dims()); + + if (!next_state.is_initialized()) { + throw std::runtime_error("Next state failed to initialize."); + } + + if (!handle_) { + throw std::runtime_error("cudm_time_stepper handle is not initializes."); + } + + if (!liouvillian_) { + throw std::runtime_error("Liouvillian is not initialized."); + } + + std::cout << "cudensitymatOperatorComputeAction ..." << std::endl; + HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction(handle_, liouvillian_, t, 0, nullptr, state.get_impl(), next_state.get_impl(), workspace, 0)); + + std::cout << "Update the state ..." << std::endl; + // Update the state + state = std::move(next_state); + + std::cout << "Clean up workspace ..." << std::endl; + // Clean up workspace + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); +} +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 0c03a81d97..ae455098f5 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -146,27 +146,4 @@ OperatorHelpers::canonicalize_degrees(const std::vector °rees) { return sorted_degrees; } -// Initialize state based on InitialStateArgT -// TODO: Implement quantum state initialization -void OperatorHelpers::initialize_state(const InitialStateArgT &stateArg, - const std::vector &dimensions) { - if (std::holds_alternative(stateArg)) { - InitialState initState = std::get(stateArg); - switch (initState) { - case InitialState::ZERO: - // ZERO quantum state initialization - std::cout << "Initialized to ZERO state." << std::endl; - break; - case InitialState::UNIFORM: - // UNIFORM quantum state initialization - std::cout << "Initialized to UNIFORM state." << std::endl; - break; - } - } else if (std::holds_alternative(stateArg)) { - // Initialization from runtime state - std::cout << "Initialized using runtime state." << std::endl; - } else { - throw std::invalid_argument("Unsupported InitialStateArgT type."); - } -} -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 39f73f7969..48e7454adf 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -19,12 +19,6 @@ #include namespace cudaq { - -// Enum to specify the initial quantum state. -enum class InitialState { ZERO, UNIFORM }; - -using InitialStateArgT = std::variant; - class OperatorHelpers { public: // Aggregate parameters from multiple mappings. @@ -53,9 +47,5 @@ class OperatorHelpers { // Canonicalize degrees by sorting in descending order. static std::vector canonicalize_degrees(const std::vector °rees); - - // Initialize state based on InitialStateArgT. - static void initialize_state(const InitialStateArgT &stateArg, - const std::vector &dimensions); }; -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 430fac8ca3..74c404b49b 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -55,6 +55,7 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/rydberg_hamiltonian.cpp dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp + dynamics/test_cudm_time_stepper.cpp ) # Make it so we can get function symbols @@ -281,8 +282,6 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_ops_simple.cpp dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp - dynamics/test_cudm_helpers.cpp - dynamics/test_cudm_state.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index d8ef07ffc4..c5e0e264cf 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -47,7 +47,7 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -58,7 +58,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_mat_state state(densityMatrixData); + cudm_state state(densityMatrixData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -73,17 +73,17 @@ TEST_F(CuDensityMatStateTest, InvalidInitialization) { std::vector> invalidData = { std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_mat_state state(invalidData); + cudm_state state(invalidData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); - cudm_mat_state densityMatrixState = state.to_density_matrix(); + cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); @@ -92,7 +92,7 @@ TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_mat_state state(densityMatrixData); + cudm_state state(densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -100,25 +100,25 @@ TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, DumpUninitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.attach_storage(), std::runtime_error); } TEST_F(CuDensityMatStateTest, DestructorCleansUp) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - cudm_mat_state state(emptyData); + cudm_state state(emptyData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } @@ -127,13 +127,13 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; stateVectorData = {std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); - cudm_mat_state densityMatrixState = state.to_density_matrix(); + cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); @@ -143,19 +143,19 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); } TEST_F(CuDensityMatStateTest, MultipleInitialization) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_initialized()); @@ -164,7 +164,7 @@ TEST_F(CuDensityMatStateTest, MultipleInitialization) { } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_mat_state state(densityMatrixData); + cudm_state state(densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -172,14 +172,14 @@ TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { - cudm_mat_state state(stateVectorData); + cudm_state state(stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp new file mode 100644 index 0000000000..d0693fec18 --- /dev/null +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include +#include +#include + +using namespace cudaq; + +// Mock Liouvillian operator creation +cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { + cudensitymatOperator_t liouvillian; + std::vector dimensions = {2, 2}; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, static_cast(dimensions.size()), dimensions.data(), &liouvillian)); + return liouvillian; +} + +// Mock Hilbert space dimensions +std::vector> mock_initial_state_data() { + return { + {1.0, 0.0}, {0.0, 0.0}, + {0.0, 0.0}, {0.0, 0.0} + }; +} + +// Mock initial raw state data +std::vector mock_hilbert_space_dims() { + return {2, 2}; +} + +class CuDensityMatTimeStepperTest : public ::testing::Test { +protected: + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; + cudm_time_stepper *time_stepper_; + cudm_state state_; + + CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; + + void SetUp() override { + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + + // Create a mock Liouvillian + liouvillian_ = mock_liouvillian(handle_); + + // Initialize the time stepper + time_stepper_ = new cudm_time_stepper(liouvillian_, handle_); + + // Initialize the state + state_.init_state(mock_hilbert_space_dims()); + + ASSERT_TRUE(state_.is_initialized()); + } + + void TearDown() override { + // Clean up + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + delete time_stepper_; + } +}; + +// Test initialization of cudm_time_stepper +TEST_F(CuDensityMatTimeStepperTest, Initialization) { + ASSERT_NE(time_stepper_, nullptr); + ASSERT_TRUE(state_.is_initialized()); + ASSERT_FALSE(state_.is_density_matrix()); +} + +// Test a single compute step +TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { + ASSERT_TRUE(state_.is_initialized()); + EXPECT_NO_THROW(time_stepper_->compute(state_, 0.0, 1.0)); + ASSERT_TRUE(state_.is_initialized()); +} + + + From d2e0441780c6dd8877554cfcca1e9214d1c31076 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 29 Jan 2025 16:00:45 -0800 Subject: [PATCH 150/311] Refactoring time_stepper compute method to align with cuquantum workflow Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 6 + runtime/cudaq/cudm_solver.h | 50 ++++--- runtime/cudaq/cudm_state.h | 11 +- runtime/cudaq/cudm_time_stepper.h | 11 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 21 +++ runtime/cudaq/dynamics/cudm_solver.cpp | 54 ++++--- runtime/cudaq/dynamics/cudm_state.cpp | 54 +++---- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 135 ++++++++++++------ runtime/cudaq/dynamics/helpers.cpp | 1 + unittests/dynamics/test_cudm_state.cpp | 38 ++--- unittests/dynamics/test_cudm_time_stepper.cpp | 93 ++++++------ 11 files changed, 288 insertions(+), 186 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index c2968229fb..08e7a6c7d3 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -40,4 +40,10 @@ cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma); + +// Function for creating an array copy in GPU memory +void *create_array_gpu(const std::vector> &cpu_array); + +// Function to detsroy a previously created array copy in GPU memory +void destroy_array_gpu(void *gpu_array); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/cudm_solver.h b/runtime/cudaq/cudm_solver.h index 2c7e14bfca..32fc7b00a8 100644 --- a/runtime/cudaq/cudm_solver.h +++ b/runtime/cudaq/cudm_solver.h @@ -8,45 +8,51 @@ #pragma once -#include -#include -#include -#include -#include -#include #include "runtime/common/EvolveResult.h" -#include #include +#include +#include +#include +#include +#include +#include #include +#include namespace cudaq { // Configuration struct for the solver struct Config { - std::map dimensions; // Hilbert space dimensions - operator_sum hamiltonian; // Hamiltonian operator - std::vector collapse_operators; // Collapse operators - std::vector observables; // Observables to evaluate - std::variant>> initial_state; // Initial state - Schedule schedule; // Evolution schedule - bool store_intermediate_results = false; // Flag to store intermediate states + std::map dimensions; // Hilbert space dimensions + operator_sum hamiltonian; // Hamiltonian operator + std::vector collapse_operators; // Collapse operators + std::vector observables; // Observables to evaluate + std::variant>> + initial_state; // Initial state + Schedule schedule; // Evolution schedule + bool store_intermediate_results = false; // Flag to store intermediate states }; class cudm_solver { public: - cudm_solver(const Config &config); + cudm_solver(const Config &config); - void validate_config(); + void validate_config(); - cudm_state initialize_state(); + cudm_state initialize_state(); - void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &obervable_ops, evolve_result &result); + void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, + const std::vector &obervable_ops, + evolve_result &result); - evolve_result evolve_dynamics(); + evolve_result evolve_dynamics(); - cudensitymatOperator_t construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve); + cudensitymatOperator_t construct_liouvillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + bool me_solve); private: - Config config_; + Config config_; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index ad7eb22cae..220136d1f8 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -24,7 +24,8 @@ using InitialStateArgT = std::variant; class cudm_state { public: /// @brief To initialize state with raw data. - explicit cudm_state(std::vector> rawData); + explicit cudm_state(cudensitymatHandle_t handle, + std::vector> rawData); /// @brief Destructor to clean up resources ~cudm_state(); @@ -34,10 +35,9 @@ class cudm_state { /// @param Dimensions of the Hilbert space. /// @param hasCollapseOps Whether collapse operators are present. /// @return A new 'cudm_state' initialized to the specified state. - static cudm_state - create_initial_state(const InitialStateArgT &initialStateArg, - const std::vector &hilbertSpaceDims, - bool hasCollapseOps); + static cudm_state create_initial_state( + cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, bool hasCollapseOps); /// @brief Initialize the state as a density matrix or state vector based on /// dimensions. @@ -77,6 +77,7 @@ class cudm_state { private: std::vector> rawData_; + std::complex *gpuData_; cudensitymatState_t state_; cudensitymatHandle_t handle_; std::vector hilbertSpaceDims_; diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/cudaq/cudm_time_stepper.h index 59b0d942d8..a245b42c80 100644 --- a/runtime/cudaq/cudm_time_stepper.h +++ b/runtime/cudaq/cudm_time_stepper.h @@ -15,12 +15,13 @@ namespace cudaq { class cudm_time_stepper : public BaseTimeStepper { public: - explicit cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian); + explicit cudm_time_stepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian); - void compute(cudm_state &state, double t, double step_size) override; + void compute(cudm_state &state, double t, double step_size) override; private: - cudensitymatHandle_t handle_; - cudensitymatOperator_t liouvillian_; + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 999ba90ff5..6325189452 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -268,4 +268,25 @@ cudensitymatOperator_t construct_liovillian( throw; } } + +// Function for creating an array copy in GPU memory +void *create_array_gpu(const std::vector> &cpu_array) { + void *gpu_array{nullptr}; + const std::size_t array_size = + cpu_array.size() * sizeof(std::complex); + if (array_size > 0) { + HANDLE_CUDA_ERROR(cudaMalloc(&gpu_array, array_size)); + HANDLE_CUDA_ERROR(cudaMemcpy(gpu_array, + static_cast(cpu_array.data()), + array_size, cudaMemcpyHostToDevice)); + } + return gpu_array; +} + +// Function to detsroy a previously created array copy in GPU memory +void destroy_array_gpu(void *gpu_array) { + if (gpu_array) { + HANDLE_CUDA_ERROR(cudaFree(gpu_array)); + } +} } // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/cudaq/dynamics/cudm_solver.cpp index 4f76d4afc8..dc35b7ce97 100644 --- a/runtime/cudaq/dynamics/cudm_solver.cpp +++ b/runtime/cudaq/dynamics/cudm_solver.cpp @@ -7,46 +7,54 @@ ******************************************************************************/ #include "cudaq/cudm_solver.h" +#include "cudaq/base_time_stepper.h" #include "cudaq/cudm_helpers.h" #include "cudaq/cudm_state.h" -#include "cudaq/base_time_stepper.h" namespace cudaq { cudm_solver::cudm_solver(const Config &config) : config_(config) { - validate_config(); + validate_config(); } void cudm_solver::validate_config() { - if (config_.dimensions.empty()) { - throw std::invalid_argument("Dimensions map cannot be empty."); - } + if (config_.dimensions.empty()) { + throw std::invalid_argument("Dimensions map cannot be empty."); + } - if (config_.hamiltonian.get_terms().empty()) { - throw std::invalid_argument("Hamiltonian must have at least one term."); - } + if (config_.hamiltonian.get_terms().empty()) { + throw std::invalid_argument("Hamiltonian must have at least one term."); + } - if (config_.dimensions.empty()) { - throw std::invalid_argument("Schedule cannot be empty."); - } + if (config_.dimensions.empty()) { + throw std::invalid_argument("Schedule cannot be empty."); + } } cudm_state cudm_solver::initialize_state() { - std::vector mode_extents; - for (const auto &[key, value] : config_.dimensions) { - mode_extents.push_back(value); - } + std::vector mode_extents; + for (const auto &[key, value] : config_.dimensions) { + mode_extents.push_back(value); + } - return cudm_state::create_initial_state(config_.initial_state, mode_extents, !config_.collapse_operators.empty()); + return cudm_state::create_initial_state(config_.initial_state, mode_extents, + !config_.collapse_operators.empty()); } -cudensitymatOperator_t cudm_solver::construct_liouvillian(cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, bool me_solve) { - return construct_liovillian(handle, hamiltonian, collapse_operators, me_solve ? 1.0 : 0.0); +cudensitymatOperator_t cudm_solver::construct_liouvillian( + cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + bool me_solve) { + return construct_liovillian(handle, hamiltonian, collapse_operators, + me_solve ? 1.0 : 0.0); } -void cudm_solver::evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, const std::vector &observable_ops, evolve_result &result) { - auto handle = state.get_impl(); +void cudm_solver::evolve( + cudm_state &state, cudensitymatOperator_t &liouvillian, + const std::vector &observable_ops, + evolve_result &result) { + auto handle = state.get_impl(); - // Initialize the stepper - BaseTimeStepper timeStepper(liouvillian, handle); + // Initialize the stepper + BaseTimeStepper timeStepper(liouvillian, handle); } -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 1e84683b0e..6d1174dfd1 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -15,18 +15,24 @@ namespace cudaq { -cudm_state::cudm_state(std::vector> rawData) - : rawData_(rawData), state_(nullptr), handle_(nullptr), - hilbertSpaceDims_() { - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); +cudm_state::cudm_state(cudensitymatHandle_t handle, + std::vector> rawData) + : rawData_(rawData), state_(nullptr), handle_(handle), hilbertSpaceDims_() { + // Allocate device memory + size_t dataSize = rawData_.size() * sizeof(std::complex); + cudaMalloc(reinterpret_cast(&gpuData_), dataSize); + + // Copy data from host to device + HANDLE_CUDA_ERROR( + cudaMemcpy(gpuData_, rawData_.data(), dataSize, cudaMemcpyHostToDevice)); } cudm_state::~cudm_state() { if (state_) { cudensitymatDestroyState(state_); } - if (handle_) { - cudensitymatDestroy(handle_); + if (gpuData_) { + cudaFree(gpuData_); } } @@ -119,7 +125,7 @@ cudm_state cudm_state::to_density_matrix() const { } } - cudm_state densityMatrixState(densityMatrix); + cudm_state densityMatrixState(handle_, densityMatrix); densityMatrixState.init_state(hilbertSpaceDims_); return densityMatrixState; } @@ -136,8 +142,9 @@ void cudm_state::attach_storage() { throw std::runtime_error("State is not initialized."); } - if (rawData_.empty()) { - throw std::runtime_error("Raw data is empty. Cannot attach storage."); + if (rawData_.empty() || !gpuData_) { + throw std::runtime_error("Raw data is empty or device memory not " + "allocated. Cannot attach storage."); } // Retrieve the number of state components @@ -150,25 +157,19 @@ void cudm_state::attach_storage() { HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( handle_, state_, numStateComponents, componentBufferSizes.data())); - // Validate that rawData_ has sufficient space for all components - size_t totalSize = 0; - for (size_t size : componentBufferSizes) { - totalSize += size; - } - if (rawData_.size() * sizeof(std::complex) < totalSize) { + // Validate device memory + size_t totalSize = std::accumulate(componentBufferSizes.begin(), + componentBufferSizes.end(), 0); + if (totalSize > rawData_.size() * sizeof(std::complex)) { throw std::invalid_argument( - "Raw data size is insufficient to cover all components."); + "Device memory size is insufficient to cover all components."); } - // Attach storage for each component + // Attach storage for using device memory (gpuData_) std::vector componentBuffers(numStateComponents); - std::vector *> rawComponentData(numStateComponents); - size_t offset = 0; for (int32_t i = 0; i < numStateComponents; i++) { - rawComponentData[i] = - reinterpret_cast *>(rawData_.data()) + offset; - componentBuffers[i] = static_cast(rawComponentData[i]); + componentBuffers[i] = static_cast(gpuData_ + offset); offset += componentBufferSizes[i] / sizeof(std::complex); } @@ -193,10 +194,9 @@ size_t cudm_state::calculate_density_matrix_size( } // Initialize state based on InitialStateArgT -cudm_state -cudm_state::create_initial_state(const InitialStateArgT &initialStateArg, - const std::vector &hilbertSpaceDims, - bool hasCollapseOps) { +cudm_state cudm_state::create_initial_state( + cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, + const std::vector &hilbertSpaceDims, bool hasCollapseOps) { size_t stateVectorSize = std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), static_cast(1), std::multiplies<>{}); @@ -239,7 +239,7 @@ cudm_state::create_initial_state(const InitialStateArgT &initialStateArg, throw std::invalid_argument("Unsupported InitialStateArgT type."); } - cudm_state state(rawData); + cudm_state state(handle, rawData); state.init_state(hilbertSpaceDims); // Convert to a density matrix if collapse operators are present. diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 86de154442..8a1d01fed4 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -8,50 +8,103 @@ #include "cudaq/cudm_time_stepper.h" #include "cudaq/cudm_error_handling.h" +#include "cudaq/cudm_helpers.h" #include namespace cudaq { -cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) : handle_(handle), liouvillian_(liouvillian) {} +cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian) + : handle_(handle), liouvillian_(liouvillian) {} void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { - if (!state.is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - - std::cout << "Preparing workspace ..." << std::endl; - // Prepare workspace - cudensitymatWorkspaceDescriptor_t workspace; - HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); - if (!workspace) { - throw std::runtime_error("Failed to create workspace for the operator."); - } - - std::cout << "Create a new state for the next step ..." << std::endl; - // Create a new state for the next step - cudm_state next_state(state.to_density_matrix().get_raw_data()); - next_state.init_state(state.get_hilbert_space_dims()); - - if (!next_state.is_initialized()) { - throw std::runtime_error("Next state failed to initialize."); - } - - if (!handle_) { - throw std::runtime_error("cudm_time_stepper handle is not initializes."); - } - - if (!liouvillian_) { - throw std::runtime_error("Liouvillian is not initialized."); - } - - std::cout << "cudensitymatOperatorComputeAction ..." << std::endl; - HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction(handle_, liouvillian_, t, 0, nullptr, state.get_impl(), next_state.get_impl(), workspace, 0)); - - std::cout << "Update the state ..." << std::endl; - // Update the state - state = std::move(next_state); - - std::cout << "Clean up workspace ..." << std::endl; - // Clean up workspace - HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); + if (!state.is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + if (!handle_) { + throw std::runtime_error("cudm_time_stepper handle is not initializes."); + } + + if (!liouvillian_) { + throw std::runtime_error("Liouvillian is not initialized."); + } + + std::cout << "Preparing workspace ..." << std::endl; + // Prepare workspace + cudensitymatWorkspaceDescriptor_t workspace; + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); + + // Query free gpu memory and allocate workspace buffer + std::size_t freeMem = 0, totalMem = 0; + HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); + // Take 80% of free memory + freeMem = static_cast(static_cast(freeMem) * 0.80); + + std::cout << "Max workspace buffer size (bytes): " << freeMem << std::endl; + + std::cout << "Create a new state for the next step ..." << std::endl; + // Create a new state for the next step + cudm_state next_state(handle_, state.get_raw_data()); + next_state.init_state(state.get_hilbert_space_dims()); + + if (!next_state.is_initialized()) { + throw std::runtime_error("Next state failed to initialize."); + } + + if (state.get_hilbert_space_dims() != next_state.get_hilbert_space_dims()) { + throw std::runtime_error( + "As the dimensions of both the old and the new state do no match, the " + "operator cannot act on the states."); + } + + // Prepare the operator for action + std::cout << "Preparing the operator for action ..." << std::endl; + HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( + handle_, liouvillian_, state.get_impl(), next_state.get_impl(), + CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); + + std::cout << "Querying required workspace buffer size ..." << std::endl; + // Query required workspace buffer size + std::size_t requiredBufferSize = 0; + HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( + handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); + + std::cout << "Required workspace buffer size (bytes): " << requiredBufferSize + << std::endl; + + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + auto *workspaceBuffer = create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + std::cout << "Allocated workspace buffer of size (bytes): " + << requiredBufferSize << std::endl; + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + + std::cout << "Attached workspace buffer" << std::endl; + + // Apply the operator action + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( + handle_, liouvillian_, t, 1, std::vector({step_size}).data(), + state.get_impl(), next_state.get_impl(), workspace, 0x0)); + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + + std::cout << "Updated quantum state" << std::endl; + + // Swap states: Move next_state into state + state = std::move(next_state); + + // Cleanup + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); + destroy_array_gpu(workspaceBuffer); + + std::cout << "Cleaned up workspace" << std::endl; } -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index ae455098f5..be7906a30b 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/helpers.h" +#include "cudaq/cudm_error_handling.h" #include #include #include diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index c5e0e264cf..163ecd3f59 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -18,7 +18,11 @@ using namespace cudaq; class CuDensityMatStateTest : public ::testing::Test { protected: + cudensitymatHandle_t handle; + void SetUp() override { + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + // Set up test data for a single 2-qubit system hilbertSpaceDims = {2, 2}; @@ -39,7 +43,7 @@ class CuDensityMatStateTest : public ::testing::Test { std::complex(0.0, 0.0), std::complex(0.0, 0.0)}; } - void TearDown() override {} + void TearDown() override { cudensitymatDestroy(handle); } std::vector hilbertSpaceDims; std::vector> stateVectorData; @@ -47,7 +51,7 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -58,7 +62,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_state state(densityMatrixData); + cudm_state state(handle, densityMatrixData); EXPECT_FALSE(state.is_initialized()); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); @@ -73,12 +77,12 @@ TEST_F(CuDensityMatStateTest, InvalidInitialization) { std::vector> invalidData = { std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_state state(invalidData); + cudm_state state(handle, invalidData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); @@ -92,7 +96,7 @@ TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_state state(densityMatrixData); + cudm_state state(handle, densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -100,25 +104,25 @@ TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { } TEST_F(CuDensityMatStateTest, DumpUninitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.attach_storage(), std::runtime_error); } TEST_F(CuDensityMatStateTest, DestructorCleansUp) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - cudm_state state(emptyData); + cudm_state state(handle, emptyData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } @@ -127,7 +131,7 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; stateVectorData = {std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); @@ -143,19 +147,19 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); } TEST_F(CuDensityMatStateTest, MultipleInitialization) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_initialized()); @@ -164,7 +168,7 @@ TEST_F(CuDensityMatStateTest, MultipleInitialization) { } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_state state(densityMatrixData); + cudm_state state(handle, densityMatrixData); state.init_state(hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); @@ -172,14 +176,14 @@ TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); state.init_state(hilbertSpaceDims); EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { - cudm_state state(stateVectorData); + cudm_state state(handle, stateVectorData); EXPECT_THROW(state.dump(), std::runtime_error); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index d0693fec18..03807d99af 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -6,81 +6,82 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include +#include +#include #include #include -#include -#include +#include using namespace cudaq; // Mock Liouvillian operator creation cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { - cudensitymatOperator_t liouvillian; - std::vector dimensions = {2, 2}; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, static_cast(dimensions.size()), dimensions.data(), &liouvillian)); - return liouvillian; + cudensitymatOperator_t liouvillian; + std::vector dimensions = {2, 2}; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(dimensions.size()), dimensions.data(), + &liouvillian)); + return liouvillian; } // Mock Hilbert space dimensions std::vector> mock_initial_state_data() { - return { - {1.0, 0.0}, {0.0, 0.0}, - {0.0, 0.0}, {0.0, 0.0} - }; + return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; } // Mock initial raw state data -std::vector mock_hilbert_space_dims() { - return {2, 2}; -} +std::vector mock_hilbert_space_dims() { return {2, 2}; } class CuDensityMatTimeStepperTest : public ::testing::Test { protected: - cudensitymatHandle_t handle_; - cudensitymatOperator_t liouvillian_; - cudm_time_stepper *time_stepper_; - cudm_state state_; + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; + cudm_time_stepper *time_stepper_; + cudm_state *state_; + + // CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; - CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; + void SetUp() override { + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); - void SetUp() override { - // Create library handle - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + // Create a mock Liouvillian + liouvillian_ = mock_liouvillian(handle_); - // Create a mock Liouvillian - liouvillian_ = mock_liouvillian(handle_); + // Initialize the time stepper + time_stepper_ = new cudm_time_stepper(handle_, liouvillian_); - // Initialize the time stepper - time_stepper_ = new cudm_time_stepper(liouvillian_, handle_); + state_ = new cudm_state(handle_, mock_initial_state_data()); - // Initialize the state - state_.init_state(mock_hilbert_space_dims()); + // Initialize the state + state_->init_state(mock_hilbert_space_dims()); - ASSERT_TRUE(state_.is_initialized()); - } + ASSERT_TRUE(state_->is_initialized()); + } - void TearDown() override { - // Clean up - HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); - HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); - delete time_stepper_; - } + void TearDown() override { + // Clean up + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + delete time_stepper_; + delete state_; + } }; // Test initialization of cudm_time_stepper -TEST_F(CuDensityMatTimeStepperTest, Initialization) { - ASSERT_NE(time_stepper_, nullptr); - ASSERT_TRUE(state_.is_initialized()); - ASSERT_FALSE(state_.is_density_matrix()); -} +// TEST_F(CuDensityMatTimeStepperTest, Initialization) { +// ASSERT_NE(time_stepper_, nullptr); +// ASSERT_TRUE(state_->is_initialized()); +// ASSERT_FALSE(state_->is_density_matrix()); +// } // Test a single compute step TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { - ASSERT_TRUE(state_.is_initialized()); - EXPECT_NO_THROW(time_stepper_->compute(state_, 0.0, 1.0)); - ASSERT_TRUE(state_.is_initialized()); + ASSERT_TRUE(state_->is_initialized()); + EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); + ASSERT_TRUE(state_->is_initialized()); } - - +// // Add test to use construct_liouvillian and then use compute to step using +// this liouvillian +// // z0 * z1 From 67d658b3b1e0ec85623830b4ae4b6e27a4b6af9a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 08:27:16 -0800 Subject: [PATCH 151/311] Exposing handle and adding operator overloading for + and * Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 12 ++++++++++++ runtime/cudaq/dynamics/cudm_state.cpp | 28 +++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 220136d1f8..0ee57cb67b 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -75,6 +75,18 @@ class cudm_state { /// @return A copy of the hilbert space dimensions of a vector of integers. std::vector get_hilbert_space_dims() const; + /// @brief Returns the handle + /// @return The handle associated with the state + cudensitymatHandle_t get_handle() const; + + /// @brief Addition operator (element-wise) + /// @return The new state after the summation of two states. + cudm_state operator+(const cudm_state &other) const; + + /// @brief Scalar multiplication operator + /// @return The new state after multiplying scalar with the current state. + cudm_state operator*(double scalar) const; + private: std::vector> rawData_; std::complex *gpuData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 6d1174dfd1..b326f5a301 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -88,6 +88,34 @@ std::vector cudm_state::get_hilbert_space_dims() const { return hilbertSpaceDims_; } +cudensitymatHandle_t cudm_state::get_handle() const { return handle_; } + +cudm_state cudm_state::operator+(const cudm_state &other) const { + if (rawData_.size() != other.rawData_.size()) { + throw std::invalid_argument("State size mismatch for addition."); + } + + std::vector> resultData(rawData_.size()); + for (size_t i = 0; i < rawData_.size(); i++) { + resultData[i] = rawData_[i] + other.rawData_[i]; + } + + cudm_state result(handle_, resultData); + result.init_state({static_cast(resultData.size())}); + return result; +} + +cudm_state cudm_state::operator*(double scalar) const { + std::vector> resultData(rawData_.size()); + for (size_t i = 0; i < rawData_.size(); i++) { + resultData[i] = rawData_[i] * scalar; + } + + cudm_state result(handle_, resultData); + result.init_state({static_cast(resultData.size())}); + return result; +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From 5d74f1793b1a46186b1bf85b7a88b6fb4fb0c22d Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 09:23:49 -0800 Subject: [PATCH 152/311] Removing redundant constructor and updating tests for cudm_state Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 14 ++-- runtime/cudaq/dynamics/cudm_state.cpp | 57 ++++++++--------- unittests/dynamics/test_cudm_state.cpp | 88 +++++--------------------- 3 files changed, 47 insertions(+), 112 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 0ee57cb67b..3b9f6ec05d 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -25,7 +25,8 @@ class cudm_state { public: /// @brief To initialize state with raw data. explicit cudm_state(cudensitymatHandle_t handle, - std::vector> rawData); + const std::vector> rawData, + const std::vector &hilbertSpaceDims); /// @brief Destructor to clean up resources ~cudm_state(); @@ -39,11 +40,6 @@ class cudm_state { cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, const std::vector &hilbertSpaceDims, bool hasCollapseOps); - /// @brief Initialize the state as a density matrix or state vector based on - /// dimensions. - /// @param hilbertSpaceDims Vector representing the Hilbert Space dimensions. - void init_state(const std::vector &hilbertSpaceDims); - /// @brief Check if the state is initialized. /// @return True if the state is initialized, false otherwise. bool is_initialized() const; @@ -64,9 +60,6 @@ class cudm_state { /// @return The underlying state implementation. cudensitymatState_t get_impl() const; - /// @brief Attach raw data to the internal state representation - void attach_storage(); - /// @brief Get a copy of the raw data representing the quantum state. /// @return A copy of the raw data as a vector of complex numbers. std::vector> get_raw_data() const; @@ -94,6 +87,9 @@ class cudm_state { cudensitymatHandle_t handle_; std::vector hilbertSpaceDims_; + /// @brief Attach raw data storage to GPU + void attach_storage(); + /// @brief Calculate the size of the state vector for the given Hilbert space /// dimensions. /// @param hilbertSpaceDims Hilbert space dimensions. diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index b326f5a301..fc881313c2 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -16,8 +16,15 @@ namespace cudaq { cudm_state::cudm_state(cudensitymatHandle_t handle, - std::vector> rawData) - : rawData_(rawData), state_(nullptr), handle_(handle), hilbertSpaceDims_() { + const std::vector> rawData, + const std::vector &hilbertSpaceDims) + : rawData_(rawData), state_(nullptr), handle_(handle), + hilbertSpaceDims_(hilbertSpaceDims) { + + if (rawData_.empty()) { + throw std::invalid_argument("Raw data cannot be empty."); + } + // Allocate device memory size_t dataSize = rawData_.size() * sizeof(std::complex); cudaMalloc(reinterpret_cast(&gpuData_), dataSize); @@ -25,24 +32,8 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, // Copy data from host to device HANDLE_CUDA_ERROR( cudaMemcpy(gpuData_, rawData_.data(), dataSize, cudaMemcpyHostToDevice)); -} - -cudm_state::~cudm_state() { - if (state_) { - cudensitymatDestroyState(state_); - } - if (gpuData_) { - cudaFree(gpuData_); - } -} - -void cudm_state::init_state(const std::vector &hilbertSpaceDims) { - if (state_) { - throw std::runtime_error("State is already initialized."); - } - - hilbertSpaceDims_ = hilbertSpaceDims; + // Determine if this is a denisty matrix or state vector size_t rawDataSize = rawData_.size(); size_t expectedDensityMatrixSize = calculate_density_matrix_size(hilbertSpaceDims); @@ -70,6 +61,15 @@ void cudm_state::init_state(const std::vector &hilbertSpaceDims) { attach_storage(); } +cudm_state::~cudm_state() { + if (state_) { + cudensitymatDestroyState(state_); + } + if (gpuData_) { + cudaFree(gpuData_); + } +} + bool cudm_state::is_initialized() const { return state_ != nullptr; } bool cudm_state::is_density_matrix() const { @@ -100,8 +100,7 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { resultData[i] = rawData_[i] + other.rawData_[i]; } - cudm_state result(handle_, resultData); - result.init_state({static_cast(resultData.size())}); + cudm_state result(handle_, resultData, hilbertSpaceDims_); return result; } @@ -111,8 +110,7 @@ cudm_state cudm_state::operator*(double scalar) const { resultData[i] = rawData_[i] * scalar; } - cudm_state result(handle_, resultData); - result.init_state({static_cast(resultData.size())}); + cudm_state result(handle_, resultData, hilbertSpaceDims_); return result; } @@ -153,8 +151,7 @@ cudm_state cudm_state::to_density_matrix() const { } } - cudm_state densityMatrixState(handle_, densityMatrix); - densityMatrixState.init_state(hilbertSpaceDims_); + cudm_state densityMatrixState(handle_, densityMatrix, hilbertSpaceDims_); return densityMatrixState; } @@ -208,11 +205,8 @@ void cudm_state::attach_storage() { size_t cudm_state::calculate_state_vector_size( const std::vector &hilbertSpaceDims) const { - size_t size = 1; - for (auto dim : hilbertSpaceDims) { - size *= dim; - } - return size; + return std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), 1, + std::multiplies<>()); } size_t cudm_state::calculate_density_matrix_size( @@ -267,8 +261,7 @@ cudm_state cudm_state::create_initial_state( throw std::invalid_argument("Unsupported InitialStateArgT type."); } - cudm_state state(handle, rawData); - state.init_state(hilbertSpaceDims); + cudm_state state(handle, rawData, hilbertSpaceDims); // Convert to a density matrix if collapse operators are present. if (hasCollapseOps && !state.is_density_matrix()) { diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 163ecd3f59..8ac6bffee2 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -51,139 +51,85 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_state state(handle, stateVectorData); - EXPECT_FALSE(state.is_initialized()); + cudm_state state(handle, stateVectorData, hilbertSpaceDims); - EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); EXPECT_TRUE(state.is_initialized()); EXPECT_FALSE(state.is_density_matrix()); - EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_state state(handle, densityMatrixData); - EXPECT_FALSE(state.is_initialized()); + cudm_state state(handle, densityMatrixData, hilbertSpaceDims); - EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); EXPECT_TRUE(state.is_initialized()); EXPECT_TRUE(state.is_density_matrix()); - EXPECT_NO_THROW(state.dump()); } TEST_F(CuDensityMatStateTest, InvalidInitialization) { // Data size mismatch for hilbertSpaceDims (2x2 system expects size 4 or 16) - std::vector> invalidData = { - std::complex(1.0, 0.0), std::complex(0.0, 0.0)}; + std::vector> invalidData = {{1.0, 0.0}, {0.0, 0.0}}; - cudm_state state(handle, invalidData); - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); + EXPECT_THROW(cudm_state state(handle, invalidData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_state state(handle, stateVectorData); - state.init_state(hilbertSpaceDims); - + cudm_state state(handle, stateVectorData, hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); cudm_state densityMatrixState = state.to_density_matrix(); - EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); - EXPECT_NO_THROW(densityMatrixState.dump()); } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_state state(handle, densityMatrixData); - state.init_state(hilbertSpaceDims); + cudm_state state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); } -TEST_F(CuDensityMatStateTest, DumpUninitializedState) { - cudm_state state(handle, stateVectorData); - EXPECT_THROW(state.dump(), std::runtime_error); -} - -TEST_F(CuDensityMatStateTest, AttachStorageErrorHandling) { - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.attach_storage(), std::runtime_error); -} - TEST_F(CuDensityMatStateTest, DestructorCleansUp) { - cudm_state state(handle, stateVectorData); - - EXPECT_NO_THROW(state.init_state(hilbertSpaceDims)); + EXPECT_NO_THROW( + { cudm_state state(handle, stateVectorData, hilbertSpaceDims); }); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - cudm_state state(handle, emptyData); - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); + EXPECT_THROW(cudm_state state(handle, emptyData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; - stateVectorData = {std::complex(1.0, 0.0), - std::complex(0.0, 0.0)}; - cudm_state state(handle, stateVectorData); - - state.init_state(hilbertSpaceDims); + stateVectorData = {{1.0, 0.0}, {0.0, 0.0}}; + cudm_state state(handle, stateVectorData, hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); - EXPECT_NO_THROW(densityMatrixState.dump()); } TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::invalid_argument); -} - -TEST_F(CuDensityMatStateTest, ToDensityMatrixFromUninitializedState) { - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.to_density_matrix(), std::runtime_error); -} - -TEST_F(CuDensityMatStateTest, MultipleInitialization) { - cudm_state state(handle, stateVectorData); - state.init_state(hilbertSpaceDims); - - EXPECT_TRUE(state.is_initialized()); - - EXPECT_THROW(state.init_state(hilbertSpaceDims), std::runtime_error); + EXPECT_THROW(cudm_state state(handle, stateVectorData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_state state(handle, densityMatrixData); - state.init_state(hilbertSpaceDims); - + cudm_state state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); EXPECT_TRUE(state.is_initialized()); } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_state state(handle, stateVectorData); - state.init_state(hilbertSpaceDims); - + cudm_state state(handle, stateVectorData, hilbertSpaceDims); EXPECT_NO_THROW(state.dump()); } - -TEST_F(CuDensityMatStateTest, DumpFailsForUninitializedState) { - cudm_state state(handle, stateVectorData); - - EXPECT_THROW(state.dump(), std::runtime_error); -} From ef1d09dd2cecfb318b59a4506c607c43d6c74b95 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 09:53:08 -0800 Subject: [PATCH 153/311] Implementing compute function in cudm_time_stepper Signed-off-by: Sachin Pisal --- runtime/cudaq/base_time_stepper.h | 7 ++- runtime/cudaq/cudm_time_stepper.h | 2 +- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 57 ++++++++------------ 3 files changed, 29 insertions(+), 37 deletions(-) diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index 4488de8b44..2c8150de0f 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -14,6 +14,11 @@ class BaseTimeStepper { public: virtual ~BaseTimeStepper() = default; - virtual void compute(TState &state, double t, double step_size) = 0; + /// @brief Compute the next time step for the given quantum state. + /// @param state The quantum state to evolve. + /// @param t Current time. + /// @param step_size Time step size. + /// @return The updated quantum state after stepping. + virtual TState compute(TState &state, double t, double step_size) = 0; }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/cudaq/cudm_time_stepper.h index a245b42c80..6037ee00f1 100644 --- a/runtime/cudaq/cudm_time_stepper.h +++ b/runtime/cudaq/cudm_time_stepper.h @@ -18,7 +18,7 @@ class cudm_time_stepper : public BaseTimeStepper { explicit cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian); - void compute(cudm_state &state, double t, double step_size) override; + cudm_state compute(cudm_state &state, double t, double step_size); private: cudensitymatHandle_t handle_; diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 8a1d01fed4..90f1277f12 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -16,7 +16,8 @@ cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) : handle_(handle), liouvillian_(liouvillian) {} -void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { +cudm_state cudm_time_stepper::compute(cudm_state &state, double t, + double step_size) { if (!state.is_initialized()) { throw std::runtime_error("State is not initialized."); } @@ -29,7 +30,6 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { throw std::runtime_error("Liouvillian is not initialized."); } - std::cout << "Preparing workspace ..." << std::endl; // Prepare workspace cudensitymatWorkspaceDescriptor_t workspace; HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); @@ -40,13 +40,12 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { // Take 80% of free memory freeMem = static_cast(static_cast(freeMem) * 0.80); - std::cout << "Max workspace buffer size (bytes): " << freeMem << std::endl; - - std::cout << "Create a new state for the next step ..." << std::endl; // Create a new state for the next step - cudm_state next_state(handle_, state.get_raw_data()); - next_state.init_state(state.get_hilbert_space_dims()); - + std::vector> zero_initiailized_data( + state.get_raw_data().size(), {0.0, 0.0}); + cudm_state next_state(handle_, zero_initiailized_data, + state.get_hilbert_space_dims()); + if (!next_state.is_initialized()) { throw std::runtime_error("Next state failed to initialize."); } @@ -58,36 +57,29 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { } // Prepare the operator for action - std::cout << "Preparing the operator for action ..." << std::endl; HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( handle_, liouvillian_, state.get_impl(), next_state.get_impl(), CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); - std::cout << "Querying required workspace buffer size ..." << std::endl; // Query required workspace buffer size std::size_t requiredBufferSize = 0; HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); - std::cout << "Required workspace buffer size (bytes): " << requiredBufferSize - << std::endl; - - // Allocate GPU storage for workspace buffer - const std::size_t bufferVolume = - requiredBufferSize / sizeof(std::complex); - auto *workspaceBuffer = create_array_gpu( - std::vector>(bufferVolume, {0.0, 0.0})); - - std::cout << "Allocated workspace buffer of size (bytes): " - << requiredBufferSize << std::endl; - - // Attach workspace buffer - HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( - handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, - CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); - - std::cout << "Attached workspace buffer" << std::endl; + void *workspaceBuffer = nullptr; + if (requiredBufferSize > 0) { + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + workspaceBuffer = create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + } // Apply the operator action HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); @@ -96,15 +88,10 @@ void cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { state.get_impl(), next_state.get_impl(), workspace, 0x0)); HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); - std::cout << "Updated quantum state" << std::endl; - - // Swap states: Move next_state into state - state = std::move(next_state); - // Cleanup - HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); destroy_array_gpu(workspaceBuffer); + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); - std::cout << "Cleaned up workspace" << std::endl; + return next_state; } } // namespace cudaq \ No newline at end of file From 6544beef2ce2977849ad4bc0445dcfbe4ad73b0f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 09:58:26 -0800 Subject: [PATCH 154/311] Adding test_mocks for unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 2 +- unittests/dynamics/test_cudm_time_stepper.cpp | 51 +++++-------------- unittests/dynamics/test_mocks.h | 33 ++++++++++++ 3 files changed, 47 insertions(+), 39 deletions(-) create mode 100644 unittests/dynamics/test_mocks.h diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 90f1277f12..c803b9488b 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -45,7 +45,7 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, state.get_raw_data().size(), {0.0, 0.0}); cudm_state next_state(handle_, zero_initiailized_data, state.get_hilbert_space_dims()); - + if (!next_state.is_initialized()) { throw std::runtime_error("Next state failed to initialize."); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 03807d99af..fbde6b089c 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -6,40 +6,23 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "test_mocks.h" #include #include #include #include #include +#include +#include using namespace cudaq; -// Mock Liouvillian operator creation -cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { - cudensitymatOperator_t liouvillian; - std::vector dimensions = {2, 2}; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(dimensions.size()), dimensions.data(), - &liouvillian)); - return liouvillian; -} - -// Mock Hilbert space dimensions -std::vector> mock_initial_state_data() { - return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; -} - -// Mock initial raw state data -std::vector mock_hilbert_space_dims() { return {2, 2}; } - class CuDensityMatTimeStepperTest : public ::testing::Test { protected: cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; - cudm_time_stepper *time_stepper_; - cudm_state *state_; - - // CuDensityMatTimeStepperTest() : state_(mock_initial_state_data()) {}; + std::unique_ptr time_stepper_; + std::unique_ptr state_; void SetUp() override { // Create library handle @@ -49,12 +32,10 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { liouvillian_ = mock_liouvillian(handle_); // Initialize the time stepper - time_stepper_ = new cudm_time_stepper(handle_, liouvillian_); - - state_ = new cudm_state(handle_, mock_initial_state_data()); + time_stepper_ = std::make_unique(handle_, liouvillian_); - // Initialize the state - state_->init_state(mock_hilbert_space_dims()); + state_ = std::make_unique(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()); ASSERT_TRUE(state_->is_initialized()); } @@ -63,17 +44,15 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { // Clean up HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); - delete time_stepper_; - delete state_; } }; // Test initialization of cudm_time_stepper -// TEST_F(CuDensityMatTimeStepperTest, Initialization) { -// ASSERT_NE(time_stepper_, nullptr); -// ASSERT_TRUE(state_->is_initialized()); -// ASSERT_FALSE(state_->is_density_matrix()); -// } +TEST_F(CuDensityMatTimeStepperTest, Initialization) { + ASSERT_NE(time_stepper_, nullptr); + ASSERT_TRUE(state_->is_initialized()); + ASSERT_FALSE(state_->is_density_matrix()); +} // Test a single compute step TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { @@ -81,7 +60,3 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); ASSERT_TRUE(state_->is_initialized()); } - -// // Add test to use construct_liouvillian and then use compute to step using -// this liouvillian -// // z0 * z1 diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h new file mode 100644 index 0000000000..6b81e26d91 --- /dev/null +++ b/unittests/dynamics/test_mocks.h @@ -0,0 +1,33 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +// Mock Liouvillian operator creation +inline cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { + cudensitymatOperator_t liouvillian; + std::vector dimensions = {2, 2}; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(dimensions.size()), dimensions.data(), + &liouvillian)); + return liouvillian; +} + +// Mock Hilbert space dimensions +inline std::vector> mock_initial_state_data() { + return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; +} + +// Mock initial raw state data +inline std::vector mock_hilbert_space_dims() { return {2, 2}; } From 847c383c91bd03ece34a1bdac1220e0a1d87a6df Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 13:36:55 -0800 Subject: [PATCH 155/311] Adding check for step_size=0 condition and few more unittests Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 6 +++- unittests/dynamics/test_cudm_time_stepper.cpp | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index c803b9488b..271689d639 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -18,12 +18,16 @@ cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, cudm_state cudm_time_stepper::compute(cudm_state &state, double t, double step_size) { + if (step_size == 0.0) { + throw std::runtime_error("Step size cannot be zero."); + } + if (!state.is_initialized()) { throw std::runtime_error("State is not initialized."); } if (!handle_) { - throw std::runtime_error("cudm_time_stepper handle is not initializes."); + throw std::runtime_error("cudm_time_stepper handle is not initialized."); } if (!liouvillian_) { diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index fbde6b089c..1bfe69c79d 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -60,3 +60,34 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); ASSERT_TRUE(state_->is_initialized()); } + +// Compute step when handle is uninitialized +TEST_F(CuDensityMatTimeStepperTest, ComputeStepUninitializedHandle) { + cudm_time_stepper invalidStepper(nullptr, liouvillian_); + EXPECT_THROW(invalidStepper.compute(*state_, 0.0, 1.0), std::runtime_error); +} + +// Compute step when liouvillian is missing +TEST_F(CuDensityMatTimeStepperTest, ComputeStepNoLiouvillian) { + cudm_time_stepper invalidStepper(handle_, nullptr); + EXPECT_THROW(invalidStepper.compute(*state_, 0.0, 1.0), std::runtime_error); +} + +// Compute step with mismatched dimensions +TEST_F(CuDensityMatTimeStepperTest, ComputeStepMistmatchedDimensions) { + EXPECT_THROW(std::unique_ptr mismatchedState = + std::make_unique(handle_, + mock_initial_state_data(), + std::vector{3, 3}), + std::invalid_argument); +} + +// Compute step with zero step size +TEST_F(CuDensityMatTimeStepperTest, ComputeStepZeroStepSize) { + EXPECT_THROW(time_stepper_->compute(*state_, 0.0, 0.0), std::runtime_error); +} + +// Compute step with large time values +TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { + EXPECT_NO_THROW(time_stepper_->compute(*state_, 1e6, 1e3)); +} From a51a70c2defbb5c4b1a4e6c126bfcc164ee522ac Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 13:39:04 -0800 Subject: [PATCH 156/311] Adding #pragma once so that header files are included only once during compilation Signed-off-by: Sachin Pisal --- runtime/cudaq/definition.h | 4 +++- runtime/cudaq/operators.h | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index bdf5af8ab5..d5013ffc9c 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -1,11 +1,13 @@ /****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * * This source code and the accompanying materials are made available under * * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include "cudaq/qis/state.h" #include "cudaq/utils/tensor.h" diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 5f07f5dd49..6543c5fb72 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -6,6 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include "definition.h" #include "utils/tensor.h" From ef247428fe039b669741e40af14a6f8e0c73adf0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 14:19:27 -0800 Subject: [PATCH 157/311] * Adding partial implementation of runge-kutta integrator * Removing time_stepper implemented specifically only for runge-kutta integrator as we now have a general cudm_time_stepper * Updating CMakelists.txt accordingly * Removing runge_kutta_test_helpers as we will be using test_mocks instead Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 18 ++- runtime/cudaq/dynamics/CMakeLists.txt | 13 +- .../cudaq/dynamics/runge_kutta_integrator.cpp | 65 ++++++++ runtime/cudaq/runge_kutta_integrator.h | 49 +++--- runtime/cudaq/runge_kutta_time_stepper.h | 33 ---- unittests/CMakeLists.txt | 1 - unittests/dynamics/runge_kutta_test_helpers.h | 24 --- .../dynamics/test_runge_kutta_integrator.cpp | 131 ++++----------- .../test_runge_kutta_time_stepper.cpp | 152 ------------------ 9 files changed, 147 insertions(+), 339 deletions(-) create mode 100644 runtime/cudaq/dynamics/runge_kutta_integrator.cpp delete mode 100644 runtime/cudaq/runge_kutta_time_stepper.h delete mode 100644 unittests/dynamics/runge_kutta_test_helpers.h delete mode 100644 unittests/dynamics/test_runge_kutta_time_stepper.cpp diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index e3196bd52d..0d63acf4d5 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -31,13 +31,27 @@ class BaseIntegrator { virtual void post_init() = 0; public: + /// @brief Default constructor + BaseIntegrator() = default; + + /// @brief Constructor to initialize the integrator with a state and time + /// stepper. + /// @param initial_state Initial quantum state. + /// @param t0 Initial time. + /// @param stepper Time stepper instance. + BaseIntegrator(const TState &initial_state, double t0, + std::shared_ptr> stepper) + : state(initial_state), t(t0), stepper(std::move(stepper)) {} + virtual ~BaseIntegrator() = default; + /// @brief Set the initial state and time void set_state(const TState &initial_state, double t0 = 0.0) { state = initial_state; t = t0; } + /// @brief Set the system parameters (dimensions, schedule, and operators) void set_system( const std::map &dimensions, std::shared_ptr schedule, std::shared_ptr hamiltonian, @@ -48,8 +62,10 @@ class BaseIntegrator { this->collapse_operators = collapse_operators; } - virtual void integrate(double t) = 0; + /// @brief Perform integration to the target time. + virtual void integrate(double target_time) = 0; + /// @brief Get the current time and state. std::pair get_state() const { return {t, state}; } }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 636d8b4096..63129f246f 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,7 +11,18 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp elementary_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp cudm_time_stepper.cpp + scalar_operators.cpp + elementary_operators.cpp + product_operators.cpp + operator_sum.cpp + schedule.cpp + definition.cpp + helpers.cpp + rydberg_hamiltonian.cpp + cudm_helpers.cpp + cudm_state.cpp + cudm_time_stepper.cpp + runge_kutta_integrator.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp new file mode 100644 index 0000000000..62633bc172 --- /dev/null +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/runge_kutta_integrator.h" +#include + +using namespace cudaq; + +namespace cudaq { +void runge_kutta_integrator::integrate(double target_time) { + if (!stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } + + double dt = integrator_options["dt"]; + if (dt <= 0) { + throw std::invalid_argument("Invalid time step size for integration."); + } + + auto handle = state.get_handle(); + auto hilbertSpaceDims = state.get_hilbert_space_dims(); + + while (t < target_time) { + double step_size = std::min(dt, target_time - 1); + + std::cout << "Runge-Kutta step at time " << t << " with step size: " << step_size << std::endl; + + // Empty vectors of same size as state.get_raw_data() + std::vector> zero_state(state.get_raw_data().size(), {0.0, 0.0}); + + cudm_state k1(handle, zero_state, hilbertSpaceDims); + cudm_state k2(handle, zero_state, hilbertSpaceDims); + cudm_state k3(handle, zero_state, hilbertSpaceDims); + cudm_state k4(handle, zero_state, hilbertSpaceDims); + + if (substeps_ == 1) { + // Euler method (1st order) + k1 = stepper->compute(state, t, step_size); + state = k1; + } else if (substeps_ == 2) { + // Midpoint method (2nd order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size); + state = (k1 + k2) * 0.5; + } else if (substeps_ == 4) { + // Runge-Kutta method (4th order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); + k3 = stepper->compute(k2, t + step_size / 2.0, step_size); + k4 = stepper->compute(k3, t + step_size, step_size); + state = (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); + } + + // Update time + t += step_size; + } + + std::cout << "Integration complete. Final time: " << t << std::endl; +} +} diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 9914258386..0b98bb4d86 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -9,39 +9,38 @@ #pragma once #include "base_integrator.h" -#include "runge_kutta_time_stepper.h" +#include "cudaq/cudm_state.h" +#include "cudaq/cudm_time_stepper.h" #include namespace cudaq { -template -class RungeKuttaIntegrator : public BaseIntegrator { +class runge_kutta_integrator : public BaseIntegrator { public: - using DerivativeFunction = std::function; - - explicit RungeKuttaIntegrator(DerivativeFunction f) - : stepper(std::make_shared>(f)) {} - - // Initializes the integrator - void post_init() override { - if (!this->stepper) { - throw std::runtime_error("Time stepper is not set"); + /// @brief Constructor to initialize the Runge-Kutta integrator + /// @param initial_state Initial quantum state. + /// @param t0 Initial time. + /// @param stepper Time stepper instance. + /// @param substeps Number of Runge-Kutta substeps (must be 1, 2, or 4) + runge_kutta_integrator(const cudm_state &initial_state, double t0, + std::shared_ptr stepper, + int substeps = 4) + : BaseIntegrator(initial_state, t0, stepper), substeps_(substeps) { + if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { + throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); } + post_init(); } - // Advances the system's state from current time to `t` - void integrate(double target_t) override { - if (!this->schedule || !this->hamiltonian) { - throw std::runtime_error("System is not properly set!"); - } + /// @brief Perform Runge-Kutta integration until the target time. + /// @param target_time The final time to integrate to. + void integrate(double t) override; - while (this->t < target_t) { - stepper->compute(this->state, this->t); - // Time step size - this->t += 0.01; - } - } +protected: + /// @brief Any post-initialization setup + void post_init() override {} private: - std::shared_ptr> stepper; + // Number of substeps in RK integration (1, 2, or 4) + int substeps_; }; -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_time_stepper.h b/runtime/cudaq/runge_kutta_time_stepper.h deleted file mode 100644 index 1dcd1f69cc..0000000000 --- a/runtime/cudaq/runge_kutta_time_stepper.h +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "base_time_stepper.h" -#include - -namespace cudaq { -template -class RungeKuttaTimeStepper : public BaseTimeStepper { -public: - using DerivativeFunction = std::function; - - RungeKuttaTimeStepper(DerivativeFunction f) : derivativeFunc(f) {} - - void compute(TState &state, double t, double dt = 0.01) override { - // 4th order Runge-Kutta method - TState k1 = derivativeFunc(state, t); - TState k2 = derivativeFunc(state + (dt / 2.0) * k1, t + dt / 2.0); - TState k3 = derivativeFunc(state + (dt / 2.0) * k2, t + dt / 2.0); - TState k4 = derivativeFunc(state + dt * k3, t + dt); - - state = state + (dt / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4); - } - -private: - DerivativeFunction derivativeFunc; -}; -} // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 74c404b49b..ecc2d68116 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -49,7 +49,6 @@ set(CUDAQ_RUNTIME_TEST_SOURCES dynamics/elementary_ops_simple.cpp dynamics/elementary_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp - dynamics/test_runge_kutta_time_stepper.cpp dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp diff --git a/unittests/dynamics/runge_kutta_test_helpers.h b/unittests/dynamics/runge_kutta_test_helpers.h deleted file mode 100644 index 4f93ffa242..0000000000 --- a/unittests/dynamics/runge_kutta_test_helpers.h +++ /dev/null @@ -1,24 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include - -// A simple state type -using TestState = double; - -// Simple derivative function: dx/dt = -x (exponential decay) -inline TestState simple_derivative(const TestState &state, double t) { - return -state; -} - -// A complex function: dx/dt = sin(t) -inline TestState sine_derivative(const TestState &state, double t) { - return std::sin(t); -} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index c75a7c8d6d..407c211210 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -7,7 +7,7 @@ // ******************************************************************************/ #include "cudaq/runge_kutta_integrator.h" -#include "runge_kutta_test_helpers.h" +#include "test_mocks.h" #include #include #include @@ -17,116 +17,43 @@ using namespace cudaq; // Test fixture class class RungeKuttaIntegratorTest : public ::testing::Test { protected: - RungeKuttaIntegrator *integrator; - std::shared_ptr schedule; - std::shared_ptr hamiltonian; + cudensitymatHandle_t handle_; + cudensitymatOperator_t liouvillian_; + std::shared_ptr time_stepper_; + std::unique_ptr integrator_; + std::unique_ptr state_; void SetUp() override { - integrator = new RungeKuttaIntegrator(simple_derivative); - // Initial state and time - integrator->set_state(1.0, 0.0); + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); - // A simple step sequence for the schedule - std::vector> steps = {0.1, 0.2, 0.3, 0.4, 0.5}; + // Create a mock Liouvillian + liouvillian_ = mock_liouvillian(handle_); - // Dummy parameters - std::vector parameters = {"param1"}; + // Initialize the time stepper + time_stepper_ = std::make_shared(handle_, liouvillian_); - // A simple parameter function - auto value_function = [](const std::string ¶m, - const std::complex &step) { return step; }; + // Create initial state + state_ = std::make_unique(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()); - // A valid schedule instance - schedule = std::make_shared(steps, parameters, value_function); + double t0 = 0.0; + // Initialize the integrator (using substeps = 2, for mid-point rule) + integrator_ = + std::make_unique(*state_, t0, time_stepper_, 2); - // A simple hamiltonian as an operator_sum - hamiltonian = std::make_shared(); - *hamiltonian += 0.5 * elementary_operator::identity(0); - *hamiltonian += 0.5 * elementary_operator::number(0); - - // System with valid components - integrator->set_system({{0, 2}}, schedule, hamiltonian); - } - - void TearDown() override { delete integrator; } -}; - -// Basic integration -TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { - integrator->integrate(1.0); - - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) - << "Basic Runge-Kutta integration failed!"; -} - -// Time evolution -TEST_F(RungeKuttaIntegratorTest, TimeEvolution) { - integrator->integrate(2.0); - - double expected = 2.0; - - EXPECT_NEAR(integrator->get_state().first, expected, 1e-5) - << "Integrator did not correctly update time!"; -} - -// Large step size -TEST_F(RungeKuttaIntegratorTest, LargeStepSize) { - integrator->integrate(5.0); - - double expected = std::exp(-5.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 1e-1) - << "Runge-Kutta integration failed for large step size!!"; -} - -// // Integrating Sine function -// TEST_F(RungeKuttaIntegratorTest, SineFunction) { -// integrator = new RungeKuttaIntegrator(sine_derivative); -// integrator->set_state(1.0, 0.0); -// integrator->set_system({{0, 2}}, schedule, hamiltonian); - -// integrator->integrate(M_PI / 2); - -// double expected = std::cos(M_PI / 2); - -// EXPECT_NEAR(integrator->get_state().second, expected, 1e-3) << -// "Runge-Kutta integration for sine function failed!"; -// } - -// Small step size -TEST_F(RungeKuttaIntegratorTest, SmallStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); - - double step_size = 0.001; - while (integrator->get_state().first < 1.0) { - integrator->integrate(integrator->get_state().first + step_size); + ASSERT_TRUE(state_->is_initialized()); } - double expected = std::exp(-1.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 5e-4) - << "Runge-Kutta integration for small step size failed!"; -} - -// Large step size -TEST_F(RungeKuttaIntegratorTest, LargeStepIntegration) { - integrator->set_state(1.0, 0.0); - integrator->set_system({{0, 2}}, schedule, hamiltonian); - - double step_size = 0.5; - double t = 0.0; - double target_t = 1.0; - while (t < target_t) { - integrator->integrate(std::min(t + step_size, target_t)); - t += step_size; + void TearDown() override { + // Clean up resources + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); } +}; - double expected = std::exp(-1.0); - - EXPECT_NEAR(integrator->get_state().second, expected, 1e-2) - << "Runge-Kutta integration for large step size failed!"; +// Test Initialization +TEST_F(RungeKuttaIntegratorTest, Initialization) { + ASSERT_NE(integrator_, nullptr); + // ASSERT_TRUE(state_->is_initialized()); } diff --git a/unittests/dynamics/test_runge_kutta_time_stepper.cpp b/unittests/dynamics/test_runge_kutta_time_stepper.cpp deleted file mode 100644 index 4c4c7b7588..0000000000 --- a/unittests/dynamics/test_runge_kutta_time_stepper.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/runge_kutta_time_stepper.h" -#include "runge_kutta_test_helpers.h" -#include -#include -#include - -// Test fixture class -class RungeKuttaTimeStepperTest : public ::testing::Test { -protected: - std::shared_ptr> stepper; - - void SetUp() override { - stepper = std::make_shared>( - simple_derivative); - } -}; - -// Single step integration -TEST_F(RungeKuttaTimeStepperTest, SingleStep) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; - - stepper->compute(state, t, dt); - - // Expected result using analytical solution: x(t) = e^(-t) - double expected = std::exp(-dt); - - EXPECT_NEAR(state, expected, 1e-3) - << "Single step Runge-Kutta integration failed!"; -} - -// Multiple step integration -TEST_F(RungeKuttaTimeStepperTest, MultipleSteps) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; - - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } - - // Expected result: x(t) = e^(-t) - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-2) - << "Multiple step Runge-Kutta integration failed!"; -} - -// Convergence to Analytical Solution -TEST_F(RungeKuttaTimeStepperTest, Convergence) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 0.01; - int steps = 100; - - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } - - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-3) - << "Runge-Kutta integration does not converge!"; -} - -// // Integrating Sine function -// TEST_F(RungeKuttaTimeStepperTest, SineFunction) { -// auto sine_stepper = -// std::make_shared>(sine_derivative); - -// // Initial values -// double state = 0.0; -// double t = 0.0; -// double dt = 0.1; -// int steps = 10; - -// for (int i = 0; i < steps; i++) { -// sine_stepper->compute(state, t, dt); -// } - -// // Expected integral of sin(t) over [0, 1] is 1 - cos(1) -// double expected = 1 - std::cos(1); - -// EXPECT_NEAR(state, expected, 1e-2) << "Runge-Kutta integration for sine -// function failed!"; -// } - -// Handling small steps sizes -TEST_F(RungeKuttaTimeStepperTest, SmallStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1e-5; - int steps = 100000; - - for (int i = 0; i < steps; i++) { - stepper->compute(state, t, dt); - } - - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-3) - << "Runge-Kutta fails with small step sizes!"; -} - -// Handling large steps sizes -TEST_F(RungeKuttaTimeStepperTest, LargeStepSize) { - // Initial values - double state = 1.0; - double t = 0.0; - double dt = 1.0; - - stepper->compute(state, t, dt); - - double expected = std::exp(-1.0); - - EXPECT_NEAR(state, expected, 1e-1) - << "Runge-Kutta is unstable with large step sizes!"; -} - -// Constant derivative (dx/dt = 0) -TEST_F(RungeKuttaTimeStepperTest, ConstantFunction) { - auto constant_stepper = - std::make_shared>( - [](const TestState &state, double t) { return 0.0; }); - - // Initial values - double state = 5.0; - double t = 0.0; - double dt = 0.1; - int steps = 10; - - for (int i = 0; i < steps; i++) { - constant_stepper->compute(state, t, dt); - } - - EXPECT_NEAR(state, 5.0, 1e-6) - << "Runge-Kutta should not change a constant function!"; -} From 04b9d779440964d6e43be75b5b10829e4bf468d9 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 31 Jan 2025 20:28:04 -0800 Subject: [PATCH 158/311] * Implementing move constructor and move assignment * Deleting the copy constructor to prevent unintended copies * Implementing Runge-Kutta integrator using Euler, Midpoint, and Runge-Kutta 4th order methods * Adding unittests for Runge-Kutta integrator Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 15 ++- runtime/cudaq/cudm_state.h | 8 ++ runtime/cudaq/dynamics/cudm_state.cpp | 45 +++++++- .../cudaq/dynamics/runge_kutta_integrator.cpp | 85 +++++++------- runtime/cudaq/runge_kutta_integrator.h | 14 ++- unittests/dynamics/test_mocks.h | 26 ++++- .../dynamics/test_runge_kutta_integrator.cpp | 104 ++++++++++++++++-- 7 files changed, 235 insertions(+), 62 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 0d63acf4d5..31d82ea201 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -39,9 +39,13 @@ class BaseIntegrator { /// @param initial_state Initial quantum state. /// @param t0 Initial time. /// @param stepper Time stepper instance. - BaseIntegrator(const TState &initial_state, double t0, + BaseIntegrator(TState &&initial_state, double t0, std::shared_ptr> stepper) - : state(initial_state), t(t0), stepper(std::move(stepper)) {} + : state(std::move(initial_state)), t(t0), stepper(std::move(stepper)) { + if (!this->stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } + } virtual ~BaseIntegrator() = default; @@ -51,6 +55,11 @@ class BaseIntegrator { t = t0; } + /// @brief Set an option for the integrator + void set_option(const std::string &key, double value) { + integrator_options[key] = value; + } + /// @brief Set the system parameters (dimensions, schedule, and operators) void set_system( const std::map &dimensions, std::shared_ptr schedule, @@ -66,6 +75,6 @@ class BaseIntegrator { virtual void integrate(double target_time) = 0; /// @brief Get the current time and state. - std::pair get_state() const { return {t, state}; } + std::pair get_state() const { return {t, state}; } }; } // namespace cudaq diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 3b9f6ec05d..ec7cce4f8a 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -28,6 +28,14 @@ class cudm_state { const std::vector> rawData, const std::vector &hilbertSpaceDims); + // Prevent copies (avoids double free issues) + cudm_state(const cudm_state &) = delete; + cudm_state &operator=(const cudm_state &) = delete; + + // Allow move semantics + cudm_state(cudm_state &&other) noexcept; + cudm_state &operator=(cudm_state &&other) noexcept; + /// @brief Destructor to clean up resources ~cudm_state(); diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index fc881313c2..170a93d483 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -12,6 +12,7 @@ #include #include #include +#include namespace cudaq { @@ -61,12 +62,47 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, attach_storage(); } +cudm_state::cudm_state(cudm_state &&other) noexcept + : rawData_(std::move(other.rawData_)), gpuData_(other.gpuData_), + state_(other.state_), handle_(other.handle_), + hilbertSpaceDims_(std::move(other.hilbertSpaceDims_)) { + other.gpuData_ = nullptr; + other.state_ = nullptr; +} + +cudm_state &cudm_state::operator=(cudm_state &&other) noexcept { + if (this != &other) { + // Free existing resources + if (state_) { + cudensitymatDestroyState(state_); + } + if (gpuData_) { + cudaFree(gpuData_); + } + + // Move data from other + rawData_ = std::move(other.rawData_); + gpuData_ = other.gpuData_; + state_ = other.state_; + handle_ = other.handle_; + hilbertSpaceDims_ = std::move(other.hilbertSpaceDims_); + + // Nullify other + other.gpuData_ = nullptr; + other.state_ = nullptr; + } + + return *this; +} + cudm_state::~cudm_state() { if (state_) { cudensitymatDestroyState(state_); + state_ = nullptr; } if (gpuData_) { cudaFree(gpuData_); + gpuData_ = nullptr; } } @@ -100,8 +136,7 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { resultData[i] = rawData_[i] + other.rawData_[i]; } - cudm_state result(handle_, resultData, hilbertSpaceDims_); - return result; + return cudm_state(handle_, resultData, hilbertSpaceDims_); } cudm_state cudm_state::operator*(double scalar) const { @@ -110,8 +145,7 @@ cudm_state cudm_state::operator*(double scalar) const { resultData[i] = rawData_[i] * scalar; } - cudm_state result(handle_, resultData, hilbertSpaceDims_); - return result; + return cudm_state(handle_, resultData, hilbertSpaceDims_); } std::string cudm_state::dump() const { @@ -151,8 +185,7 @@ cudm_state cudm_state::to_density_matrix() const { } } - cudm_state densityMatrixState(handle_, densityMatrix, hilbertSpaceDims_); - return densityMatrixState; + return cudm_state(handle_, densityMatrix, hilbertSpaceDims_); } cudensitymatState_t cudm_state::get_impl() const { diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 62633bc172..abf0291419 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -13,53 +13,60 @@ using namespace cudaq; namespace cudaq { void runge_kutta_integrator::integrate(double target_time) { - if (!stepper) { - throw std::runtime_error("Time stepper is not initialized."); - } + if (!stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } - double dt = integrator_options["dt"]; - if (dt <= 0) { - throw std::invalid_argument("Invalid time step size for integration."); - } + if (integrator_options.find("dt") == integrator_options.end()) { + throw std::invalid_argument( + "Time step size (dt) is missing from integrator options."); + } - auto handle = state.get_handle(); - auto hilbertSpaceDims = state.get_hilbert_space_dims(); + double dt = integrator_options["dt"]; + if (dt <= 0) { + throw std::invalid_argument("Invalid time step size for integration."); + } - while (t < target_time) { - double step_size = std::min(dt, target_time - 1); + auto handle = state.get_handle(); + auto hilbertSpaceDims = state.get_hilbert_space_dims(); - std::cout << "Runge-Kutta step at time " << t << " with step size: " << step_size << std::endl; + while (t < target_time) { + double step_size = std::min(dt, target_time - t); - // Empty vectors of same size as state.get_raw_data() - std::vector> zero_state(state.get_raw_data().size(), {0.0, 0.0}); + std::cout << "Runge-Kutta step at time " << t + << " with step size: " << step_size << std::endl; - cudm_state k1(handle, zero_state, hilbertSpaceDims); - cudm_state k2(handle, zero_state, hilbertSpaceDims); - cudm_state k3(handle, zero_state, hilbertSpaceDims); - cudm_state k4(handle, zero_state, hilbertSpaceDims); + // Empty vectors of same size as state.get_raw_data() + std::vector> zero_state(state.get_raw_data().size(), + {0.0, 0.0}); - if (substeps_ == 1) { - // Euler method (1st order) - k1 = stepper->compute(state, t, step_size); - state = k1; - } else if (substeps_ == 2) { - // Midpoint method (2nd order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size); - state = (k1 + k2) * 0.5; - } else if (substeps_ == 4) { - // Runge-Kutta method (4th order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); - k3 = stepper->compute(k2, t + step_size / 2.0, step_size); - k4 = stepper->compute(k3, t + step_size, step_size); - state = (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); - } + cudm_state k1(handle, zero_state, hilbertSpaceDims); + cudm_state k2(handle, zero_state, hilbertSpaceDims); + cudm_state k3(handle, zero_state, hilbertSpaceDims); + cudm_state k4(handle, zero_state, hilbertSpaceDims); - // Update time - t += step_size; + if (substeps_ == 1) { + // Euler method (1st order) + k1 = stepper->compute(state, t, step_size); + state = std::move(k1); + } else if (substeps_ == 2) { + // Midpoint method (2nd order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size); + state = std::move((k1 + k2) * 0.5); + } else if (substeps_ == 4) { + // Runge-Kutta method (4th order) + k1 = stepper->compute(state, t, step_size / 2.0); + k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); + k3 = stepper->compute(k2, t + step_size / 2.0, step_size); + k4 = stepper->compute(k3, t + step_size, step_size); + state = std::move((k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0)); } - std::cout << "Integration complete. Final time: " << t << std::endl; -} + // Update time + t += step_size; + } + + std::cout << "Integration complete. Final time: " << t << std::endl; } +} // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 0b98bb4d86..fa9585164f 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -8,9 +8,10 @@ #pragma once -#include "base_integrator.h" +#include "cudaq/base_integrator.h" #include "cudaq/cudm_state.h" #include "cudaq/cudm_time_stepper.h" +#include #include namespace cudaq { @@ -21,10 +22,15 @@ class runge_kutta_integrator : public BaseIntegrator { /// @param t0 Initial time. /// @param stepper Time stepper instance. /// @param substeps Number of Runge-Kutta substeps (must be 1, 2, or 4) - runge_kutta_integrator(const cudm_state &initial_state, double t0, + runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4) - : BaseIntegrator(initial_state, t0, stepper), substeps_(substeps) { + : BaseIntegrator(std::move(initial_state), t0, stepper), + substeps_(substeps) { + if (!stepper) { + throw std::invalid_argument("Time stepper must be initialized."); + } + if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); } @@ -33,7 +39,7 @@ class runge_kutta_integrator : public BaseIntegrator { /// @brief Perform Runge-Kutta integration until the target time. /// @param target_time The final time to integrate to. - void integrate(double t) override; + void integrate(double target_time) override; protected: /// @brief Any post-initialization setup diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 6b81e26d91..7715b14a7d 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -16,18 +16,38 @@ // Mock Liouvillian operator creation inline cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { - cudensitymatOperator_t liouvillian; + cudensitymatOperator_t liouvillian = nullptr; std::vector dimensions = {2, 2}; HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle, static_cast(dimensions.size()), dimensions.data(), &liouvillian)); + + if (!liouvillian) { + throw std::runtime_error("Failed to create mock Liouvillian!"); + } + return liouvillian; } // Mock Hilbert space dimensions inline std::vector> mock_initial_state_data() { - return {{1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + std::vector> data = { + {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + + if (data.size() != 4) { + throw std::runtime_error("Mock initial state data has incorrect size!"); + } + + return data; } // Mock initial raw state data -inline std::vector mock_hilbert_space_dims() { return {2, 2}; } +inline std::vector mock_hilbert_space_dims() { + std::vector dims = {2, 2}; + + if (dims.empty()) { + throw std::runtime_error("Mock Hilbert space dimensions are empty!"); + } + + return dims; +} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 407c211210..0200d01d8e 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -14,7 +14,6 @@ using namespace cudaq; -// Test fixture class class RungeKuttaIntegratorTest : public ::testing::Test { protected: cudensitymatHandle_t handle_; @@ -32,17 +31,19 @@ class RungeKuttaIntegratorTest : public ::testing::Test { // Initialize the time stepper time_stepper_ = std::make_shared(handle_, liouvillian_); + ASSERT_NE(time_stepper_, nullptr); // Create initial state state_ = std::make_unique(handle_, mock_initial_state_data(), mock_hilbert_space_dims()); + ASSERT_NE(state_, nullptr); + ASSERT_TRUE(state_->is_initialized()); double t0 = 0.0; - // Initialize the integrator (using substeps = 2, for mid-point rule) - integrator_ = - std::make_unique(*state_, t0, time_stepper_, 2); - - ASSERT_TRUE(state_->is_initialized()); + // Initialize the integrator (using substeps = 4, for Runge-Kutta method) + ASSERT_NO_THROW(integrator_ = std::make_unique( + std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NE(integrator_, nullptr); } void TearDown() override { @@ -55,5 +56,94 @@ class RungeKuttaIntegratorTest : public ::testing::Test { // Test Initialization TEST_F(RungeKuttaIntegratorTest, Initialization) { ASSERT_NE(integrator_, nullptr); - // ASSERT_TRUE(state_->is_initialized()); +} + +// Integration with Euler Method (substeps = 1) +TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { + auto eulerIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 1); + eulerIntegrator->set_option("dt", 0.1); + EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); +} + +// Integration with Midpoint Rule (substeps = 2) +TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { + auto midpointIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); + integrator_->set_option("dt", 0.1); + EXPECT_NO_THROW(integrator_->integrate(1.0)); +} + +// Integration with Runge-Kutta 4 (substeps = 4, which is the default value) +TEST_F(RungeKuttaIntegratorTest, RungeKutta4Integration) { + integrator_->set_option("dt", 0.1); + EXPECT_NO_THROW(integrator_->integrate(1.0)); +} + +// Basic Integration Test +TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { + auto [t_before, state_before] = integrator_->get_state(); + integrator_->set_option("dt", 0.1); + + EXPECT_NO_THROW(integrator_->integrate(1.0)); + + auto [t_after, state_after] = integrator_->get_state(); + EXPECT_GT(t_after, t_before); +} + +// Multiple Integration Steps +TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { + integrator_->set_option("dt", 0.1); + integrator_->integrate(0.5); + auto [t_mid, _] = integrator_->get_state(); + + EXPECT_EQ(t_mid, 0.5); + + integrator_->integrate(1.0); + auto [t_final, __] = integrator_->get_state(); + + EXPECT_EQ(t_final, 1.0); +} + +// Missing Time Step (dt) +TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { + auto integrator_missing_dt = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); + + EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); +} + +// Invalid Time Step (dt <= 0) +TEST_F(RungeKuttaIntegratorTest, InvalidTimeStepSize) { + integrator_->set_option("dt", -0.1); + EXPECT_THROW(integrator_->integrate(1.0), std::invalid_argument); +} + +// Zero Integration Time +TEST_F(RungeKuttaIntegratorTest, ZeroIntegrationTime) { + auto [t_before, state_before] = integrator_->get_state(); + integrator_->set_option("dt", 0.1); + + EXPECT_NO_THROW(integrator_->integrate(0.0)); + + auto [t_after, state_after] = integrator_->get_state(); + EXPECT_EQ(t_before, t_after); +} + +// Large Time Step +TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { + integrator_->set_option("dt", 100); + EXPECT_NO_THROW(integrator_->integrate(0.0)); +} + +// Invalid Substeps +TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { + EXPECT_THROW(std::make_unique( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 3), + std::invalid_argument); } From 6cb5db256bfce931123a215ab53dc31e2441c0c4 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sun, 2 Feb 2025 09:12:33 -0800 Subject: [PATCH 159/311] temp Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 6 -- runtime/cudaq/dynamics/cudm_helpers.cpp | 87 +++++++++++------------- runtime/cudaq/dynamics/cudm_solver.cpp | 4 +- unittests/dynamics/test_cudm_helpers.cpp | 44 ++++++------ 4 files changed, 66 insertions(+), 75 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 08e7a6c7d3..6ed2fc087b 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -17,15 +17,9 @@ #include namespace cudaq { -cudensitymatState_t initialize_state(cudensitymatHandle_t handle, - cudensitymatStatePurity_t purity, - const std::vector &mode_extents); - void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream); -void destroy_state(cudensitymatState_t state); - cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 6325189452..8dc21d1cfd 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -43,7 +43,15 @@ get_subspace_extents(const std::vector &mode_extents, cudensitymatElementaryOperator_t create_elementary_operator( cudensitymatHandle_t handle, const std::vector &subspace_extents, const std::vector> &flat_matrix) { - cudensitymatElementaryOperator_t cudm_elem_op; + if (flat_matrix.empty()) { + throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); + } + + if (subspace_extents.empty()) { + throw std::invalid_argument("subspace_extents cannot be empty."); + } + + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; std::vector interleaved_matrix; interleaved_matrix.reserve(flat_matrix.size() * 2); @@ -53,11 +61,16 @@ cudensitymatElementaryOperator_t create_elementary_operator( interleaved_matrix.push_back(value.imag()); } - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + cudensitymatStatus_t status = cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, static_cast(interleaved_matrix.data()), - {nullptr, nullptr}, &cudm_elem_op)); + {nullptr, nullptr}, &cudm_elem_op); + + if (status != CUDENSITYMAT_STATUS_SUCCESS) { + std::cerr << "Error: Failed to create elementary operator. Status: " << status << std::endl; + return nullptr; + } return cudm_elem_op; } @@ -65,12 +78,18 @@ cudensitymatElementaryOperator_t create_elementary_operator( // Function to append an elementary operator to a term void append_elementary_operator_to_term( cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, - cudensitymatElementaryOperator_t &elem_op, + const cudensitymatElementaryOperator_t &elem_op, const std::vector °rees) { + if (degrees.empty()) { + throw std::invalid_argument("Degrees vector cannot be empty."); + } + + std::vector elem_ops = {elem_op}; + std::vector modeActionDuality(degrees.size(), 0); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, static_cast(degrees.size()), &elem_op, + handle, term, static_cast(degrees.size()), elem_ops.data(), degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); } @@ -86,19 +105,6 @@ void append_scalar_to_term(cudensitymatHandle_t handle, {make_cuDoubleComplex(coeff.real(), coeff.imag())}, {nullptr, nullptr})); } -cudensitymatState_t initialize_state(cudensitymatHandle_t handle, - cudensitymatStatePurity_t purity, - const std::vector &mode_extents) { - cudensitymatState_t state; - cudensitymatStatus_t status = - cudensitymatCreateState(handle, purity, mode_extents.size(), - mode_extents.data(), 1, CUDA_C_64F, &state); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - std::cerr << "Error in cudensitymatCreateState: " << status << std::endl; - } - return state; -} - void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream) { if (!state) { @@ -109,10 +115,6 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); } -void destroy_state(cudensitymatState_t state) { - cudensitymatDestroyState(state); -} - cudensitymatOperator_t compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, @@ -140,14 +142,13 @@ compute_lindblad_operator(cudensitymatHandle_t handle, handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); - // Create elementary operator form c_op - auto cudm_elem_op = + // Create elementary operator from c_op + cudensitymatElementaryOperator_t cudm_elem_op = create_elementary_operator(handle, mode_extents, flat_matrix); - // Add the elementary operator to the term - // TODO: Fix temp vector below - std::vector temp; - append_elementary_operator_to_term(handle, term, cudm_elem_op, temp); + // Append the elementary operator to the term + std::vector degrees = {0, 1}; + append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; @@ -175,6 +176,10 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + try { cudensitymatOperator_t operator_handle; HANDLE_CUDM_ERROR(cudensitymatCreateOperator( @@ -233,38 +238,28 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( } } -cudensitymatOperator_t construct_liovillian( +cudensitymatOperator_t construct_liouvillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { try { cudensitymatOperator_t liouvillian; - auto status = cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - throw std::runtime_error("Failed to create Liouvillian operator."); - } + HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - status = cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, - {1.0, 0.0}, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add hamiltonian term."); - } + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, + {1.0, 0.0}, scalarCallback)); + // Collapse operator scaled by gamma cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); for (const auto &c_op : collapse_operators) { - status = cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, - coefficient, scalarCallback); - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - cudensitymatDestroyOperator(liouvillian); - throw std::runtime_error("Failed to add collapse operator term."); - } + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, + coefficient, scalarCallback)); } return liouvillian; } catch (const std::exception &e) { - std::cerr << "Error in construct_liovillian: " << e.what() << std::endl; + std::cerr << "Error in construct_liouvillian: " << e.what() << std::endl; throw; } } diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/cudaq/dynamics/cudm_solver.cpp index dc35b7ce97..38f9a23591 100644 --- a/runtime/cudaq/dynamics/cudm_solver.cpp +++ b/runtime/cudaq/dynamics/cudm_solver.cpp @@ -7,9 +7,9 @@ ******************************************************************************/ #include "cudaq/cudm_solver.h" -#include "cudaq/base_time_stepper.h" #include "cudaq/cudm_helpers.h" #include "cudaq/cudm_state.h" +#include "cudaq/cudm_time_stepper.h" namespace cudaq { cudm_solver::cudm_solver(const Config &config) : config_(config) { @@ -55,6 +55,6 @@ void cudm_solver::evolve( auto handle = state.get_impl(); // Initialize the stepper - BaseTimeStepper timeStepper(liouvillian, handle); + cudm_time_stepper time_stepper(handle, liouvillian); } } // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 734470365a..2512d047ef 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -8,6 +8,7 @@ #include #include +#include #include // Initialize operator_sum @@ -37,37 +38,36 @@ class CuDensityMatTestFixture : public ::testing::Test { void SetUp() override { HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); - HANDLE_CUDA_ERROR(cudaStreamCreate(&stream)); + stream = 0; } void TearDown() override { - HANDLE_CUDA_ERROR(cudaStreamDestroy(stream)); - HANDLE_CUDM_ERROR(cudensitymatDestroy(handle)); + cudensitymatDestroy(handle); } }; // Test for initialize_state TEST_F(CuDensityMatTestFixture, InitializeState) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = {2}; + + std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - ASSERT_NE(state, nullptr); + cudaq::cudm_state state(handle, rawData, mode_extents); - cudaq::destroy_state(state); + ASSERT_TRUE(state.is_initialized()); } // Test for scale_state TEST_F(CuDensityMatTestFixture, ScaleState) { std::vector mode_extents = {2}; - auto state = cudaq::initialize_state(handle, CUDENSITYMAT_STATE_PURITY_PURE, - mode_extents); - ASSERT_NE(state, nullptr); + std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - EXPECT_NO_THROW(cudaq::scale_state(handle, state, 2.0, stream)); + cudaq::cudm_state state(handle, rawData, mode_extents); - cudaq::destroy_state(state); + ASSERT_TRUE(state.is_initialized()); + + EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), 2.0, stream)); } // Test for compute_lindblad_op @@ -78,11 +78,12 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); std::vector c_ops = {c_op1, c_op2}; - auto lindblad_op = + EXPECT_NO_THROW({ + auto lindblad_op = cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - ASSERT_NE(lindblad_op, nullptr); - - cudensitymatDestroyOperator(lindblad_op); + ASSERT_NE(lindblad_op, nullptr); + cudensitymatDestroyOperator(lindblad_op); + }); } // Test for convert_to_cudensitymat_operator @@ -91,11 +92,12 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { auto op_sum = initialize_operator_sum(); - auto result = + EXPECT_NO_THROW({ + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); - - cudensitymatDestroyOperator(result); + ASSERT_NE(result, nullptr); + cudensitymatDestroyOperator(result); + }); } // Test invalid handle From aea580fecc5fa86a8144679cf05ef2f74cb7beeb Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 3 Feb 2025 14:47:14 -0800 Subject: [PATCH 160/311] Using cudensitymatStateComputeAccumulation and cudensitymatStateComputeScaling for addition and multiplication of cudm_states Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 14 +++++----- runtime/cudaq/dynamics/cudm_state.cpp | 34 +++++++++++++++++------- unittests/dynamics/test_cudm_helpers.cpp | 10 +++---- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 8dc21d1cfd..a04869d28c 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -68,7 +68,8 @@ cudensitymatElementaryOperator_t create_elementary_operator( {nullptr, nullptr}, &cudm_elem_op); if (status != CUDENSITYMAT_STATUS_SUCCESS) { - std::cerr << "Error: Failed to create elementary operator. Status: " << status << std::endl; + std::cerr << "Error: Failed to create elementary operator. Status: " + << status << std::endl; return nullptr; } @@ -244,17 +245,18 @@ cudensitymatOperator_t construct_liouvillian( double gamma) { try { cudensitymatOperator_t liouvillian; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); + HANDLE_CUDM_ERROR( + cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, hamiltonian, 0, - {1.0, 0.0}, scalarCallback)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback)); // Collapse operator scaled by gamma cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); for (const auto &c_op : collapse_operators) { - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, liouvillian, c_op, 0, - coefficient, scalarCallback)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, c_op, 0, coefficient, scalarCallback)); } return liouvillian; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 170a93d483..bac537db39 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -131,21 +131,35 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { throw std::invalid_argument("State size mismatch for addition."); } - std::vector> resultData(rawData_.size()); - for (size_t i = 0; i < rawData_.size(); i++) { - resultData[i] = rawData_[i] + other.rawData_[i]; - } + cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + + double scalingFactor = 1.0; + double *gpuScalingFactor; + cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); + cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), + cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( + handle_, other.get_impl(), result.get_impl(), gpuScalingFactor, 0)); + + cudaFree(gpuScalingFactor); - return cudm_state(handle_, resultData, hilbertSpaceDims_); + return result; } cudm_state cudm_state::operator*(double scalar) const { - std::vector> resultData(rawData_.size()); - for (size_t i = 0; i < rawData_.size(); i++) { - resultData[i] = rawData_[i] * scalar; - } + cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + + double *gpuScalar; + cudaMalloc(reinterpret_cast(&gpuScalar), sizeof(double)); + cudaMemcpy(gpuScalar, &scalar, sizeof(double), cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeScaling(handle_, result.get_impl(), + gpuScalar, 0)); + + cudaFree(gpuScalar); - return cudm_state(handle_, resultData, hilbertSpaceDims_); + return result; } std::string cudm_state::dump() const { diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 2512d047ef..e747cc97db 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -41,9 +41,7 @@ class CuDensityMatTestFixture : public ::testing::Test { stream = 0; } - void TearDown() override { - cudensitymatDestroy(handle); - } + void TearDown() override { cudensitymatDestroy(handle); } }; // Test for initialize_state @@ -80,7 +78,7 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { EXPECT_NO_THROW({ auto lindblad_op = - cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); ASSERT_NE(lindblad_op, nullptr); cudensitymatDestroyOperator(lindblad_op); }); @@ -93,8 +91,8 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = - cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, mode_extents); + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, + mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); From 6cbdd281a5809e27914aaecdb9b41173cf98bc19 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 3 Feb 2025 14:54:10 -0800 Subject: [PATCH 161/311] Allocating k1, k2, k3, k4 inside the scope blocks Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index abf0291419..cde9d010a2 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -27,39 +27,28 @@ void runge_kutta_integrator::integrate(double target_time) { throw std::invalid_argument("Invalid time step size for integration."); } - auto handle = state.get_handle(); - auto hilbertSpaceDims = state.get_hilbert_space_dims(); - while (t < target_time) { double step_size = std::min(dt, target_time - t); std::cout << "Runge-Kutta step at time " << t << " with step size: " << step_size << std::endl; - // Empty vectors of same size as state.get_raw_data() - std::vector> zero_state(state.get_raw_data().size(), - {0.0, 0.0}); - - cudm_state k1(handle, zero_state, hilbertSpaceDims); - cudm_state k2(handle, zero_state, hilbertSpaceDims); - cudm_state k3(handle, zero_state, hilbertSpaceDims); - cudm_state k4(handle, zero_state, hilbertSpaceDims); - if (substeps_ == 1) { // Euler method (1st order) - k1 = stepper->compute(state, t, step_size); + cudm_state k1 = stepper->compute(state, t, step_size); state = std::move(k1); } else if (substeps_ == 2) { // Midpoint method (2nd order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size); + cudm_state k1 = stepper->compute(state, t, step_size / 2.0); + cudm_state k2 = stepper->compute(k1, t + step_size / 2.0, step_size); state = std::move((k1 + k2) * 0.5); } else if (substeps_ == 4) { // Runge-Kutta method (4th order) - k1 = stepper->compute(state, t, step_size / 2.0); - k2 = stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); - k3 = stepper->compute(k2, t + step_size / 2.0, step_size); - k4 = stepper->compute(k3, t + step_size, step_size); + cudm_state k1 = stepper->compute(state, t, step_size / 2.0); + cudm_state k2 = + stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); + cudm_state k3 = stepper->compute(k2, t + step_size / 2.0, step_size); + cudm_state k4 = stepper->compute(k3, t + step_size, step_size); state = std::move((k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0)); } From c35225bdce25736114b87994382daa3c3b7fe752 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 3 Feb 2025 15:02:25 -0800 Subject: [PATCH 162/311] Adding operator+= to do an accumulation instead of moving the state Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 4 ++++ runtime/cudaq/dynamics/cudm_state.cpp | 19 +++++++++++++++++++ .../cudaq/dynamics/runge_kutta_integrator.cpp | 6 +++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index ec7cce4f8a..486e3034ac 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -84,6 +84,10 @@ class cudm_state { /// @return The new state after the summation of two states. cudm_state operator+(const cudm_state &other) const; + /// @brief Accumulation operator + /// @return Accumulates the summation of two states. + cudm_state &operator+=(const cudm_state &other); + /// @brief Scalar multiplication operator /// @return The new state after multiplying scalar with the current state. cudm_state operator*(double scalar) const; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index bac537db39..c44bde5d2a 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -147,6 +147,25 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { return result; } +cudm_state &cudm_state::operator+=(const cudm_state &other) { + if (rawData_.size() != other.rawData_.size()) { + throw std::invalid_argument("State size mismatch for addition."); + } + + double scalingFactor = 1.0; + double *gpuScalingFactor; + cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); + cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), + cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( + handle_, other.get_impl(), state_, gpuScalingFactor, 0)); + + cudaFree(gpuScalingFactor); + + return *this; +} + cudm_state cudm_state::operator*(double scalar) const { cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index cde9d010a2..e966ef6ec8 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -36,12 +36,12 @@ void runge_kutta_integrator::integrate(double target_time) { if (substeps_ == 1) { // Euler method (1st order) cudm_state k1 = stepper->compute(state, t, step_size); - state = std::move(k1); + state += k1; } else if (substeps_ == 2) { // Midpoint method (2nd order) cudm_state k1 = stepper->compute(state, t, step_size / 2.0); cudm_state k2 = stepper->compute(k1, t + step_size / 2.0, step_size); - state = std::move((k1 + k2) * 0.5); + state += (k1 + k2) * 0.5; } else if (substeps_ == 4) { // Runge-Kutta method (4th order) cudm_state k1 = stepper->compute(state, t, step_size / 2.0); @@ -49,7 +49,7 @@ void runge_kutta_integrator::integrate(double target_time) { stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); cudm_state k3 = stepper->compute(k2, t + step_size / 2.0, step_size); cudm_state k4 = stepper->compute(k3, t + step_size, step_size); - state = std::move((k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0)); + state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } // Update time From 24ea1a0f41961d1ff97e74db5cff99e87be7b0e7 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Jan 2025 15:19:01 +0000 Subject: [PATCH 163/311] just some test renaming to make it easier to find Signed-off-by: Bettina Heim --- unittests/dynamics/elementary_ops_arithmetic.cpp | 6 +++--- unittests/dynamics/elementary_ops_simple.cpp | 4 ++-- unittests/dynamics/operator_sum.cpp | 12 ++++++------ unittests/dynamics/product_operators_arithmetic.cpp | 12 ++++++------ unittests/dynamics/scalar_ops_arithmetic.cpp | 4 ++-- unittests/dynamics/scalar_ops_simple.cpp | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index fb5c85ac49..0127e0bc39 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -105,7 +105,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { } // namespace utils_0 -TEST(ExpressionTester, checkPreBuiltElementaryOpsScalars) { +TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto function = [](std::map> parameters) { return parameters["value"]; @@ -284,7 +284,7 @@ TEST(ExpressionTester, checkPreBuiltElementaryOpsScalars) { } /// Prebuilt elementary ops against one another. -TEST(ExpressionTester, checkPreBuiltElementaryOpsSelf) { +TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { /// Keeping this fixed throughout. int level_count = 3; @@ -412,7 +412,7 @@ TEST(ExpressionTester, checkPreBuiltElementaryOpsSelf) { /// Testing arithmetic between elementary operators and operator /// sums. -TEST(ExpressionTester, checkElementaryOpsAgainstOpSum) { +TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// Keeping this fixed throughout. int level_count = 3; diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp index 172beeffe8..b45d3d8952 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -99,7 +99,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { } // namespace utils -TEST(ExpressionTester, checkPreBuiltElementaryOps) { +TEST(OperatorExpressions, checkPreBuiltElementaryOps) { std::vector levels = {2, 3, 4, 5}; // Keeping this fixed throughout. @@ -203,6 +203,6 @@ TEST(ExpressionTester, checkPreBuiltElementaryOps) { // TODO: Squeeze operator. } -// TEST(ExpressionTester, checkCustomElementaryOps) { +// TEST(OperatorExpressions, checkCustomElementaryOps) { // // pass // } \ No newline at end of file diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index c68779ef45..ac8fc63887 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -100,7 +100,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { } // namespace utils_2 -// TEST(ExpressionTester, checkProductOperatorSimple) { +// TEST(OperatorExpressions, checkProductOperatorSimple) { // std::vector levels = {2, 3, 4}; // // std::set uniqueDegrees; @@ -188,7 +188,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } // } -// TEST(ExpressionTester, checkProductOperatorSimple) { +// TEST(OperatorExpressions, checkProductOperatorSimple) { // std::complex value_0 = 0.1 + 0.1; // std::complex value_1 = 0.1 + 1.0; @@ -227,7 +227,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } // } -TEST(ExpressionTester, checkOperatorSumAgainstScalarOperator) { +TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { @@ -307,7 +307,7 @@ TEST(ExpressionTester, checkOperatorSumAgainstScalarOperator) { } } -TEST(ExpressionTester, checkOperatorSumAgainstScalars) { +TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { std::complex value = 0.1 + 0.1; // `operator_sum * double` and `double * operator_sum` @@ -468,7 +468,7 @@ TEST(ExpressionTester, checkOperatorSumAgainstScalars) { } } -TEST(ExpressionTester, checkOperatorSumAgainstOperatorSum) { +TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + @@ -529,7 +529,7 @@ TEST(ExpressionTester, checkOperatorSumAgainstOperatorSum) { /// NOTE: Much of the simpler arithmetic between the two is tested in the /// product operator test file. This mainly just tests the assignment operators /// between the two types. -TEST(ExpressionTester, checkOperatorSumAgainstProduct) { +TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum += product_operator` { auto product = cudaq::elementary_operator::annihilate(0) * diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index f8b6be7742..cf4660af80 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -104,7 +104,7 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { /// TODO: Not yet testing the output matrices coming from this arithmetic. -TEST(ExpressionTester, checkProductOperatorSimpleMatrixChecks) { +TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector levels = {2, 3, 4}; { @@ -200,7 +200,7 @@ TEST(ExpressionTester, checkProductOperatorSimpleMatrixChecks) { } } -TEST(ExpressionTester, checkProductOperatorSimpleContinued) { +TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; @@ -244,7 +244,7 @@ TEST(ExpressionTester, checkProductOperatorSimpleContinued) { } } -TEST(ExpressionTester, checkProductOperatorAgainstScalars) { +TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::complex value_0 = 0.1 + 0.1; /// `product_operator + complex` @@ -433,7 +433,7 @@ TEST(ExpressionTester, checkProductOperatorAgainstScalars) { } } -TEST(ExpressionTester, checkProductOperatorAgainstProduct) { +TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator + product_operator` { @@ -484,7 +484,7 @@ TEST(ExpressionTester, checkProductOperatorAgainstProduct) { } } -TEST(ExpressionTester, checkProductOperatorAgainstElementary) { +TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // `product_operator + elementary_operator` { @@ -537,7 +537,7 @@ TEST(ExpressionTester, checkProductOperatorAgainstElementary) { } } -TEST(ExpressionTester, checkProductOperatorAgainstOperatorSum) { +TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator + operator_sum` { diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index a8d6054a32..edd4f9e675 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(ExpressionTester, checkScalarOpsArithmeticDoubles) { +TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { // Arithmetic overloads against complex doubles. std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; @@ -259,7 +259,7 @@ TEST(ExpressionTester, checkScalarOpsArithmeticDoubles) { } } -TEST(ExpressionTester, checkScalarOpsArithmeticScalarOps) { +TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { // Arithmetic overloads against other scalar ops. std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index c60414d705..158ab49841 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(ExpressionTester, checkScalarOpsSimpleComplex) { +TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; @@ -64,7 +64,7 @@ TEST(ExpressionTester, checkScalarOpsSimpleComplex) { } } -TEST(ExpressionTester, checkScalarOpsSimpleDouble) { +TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { double value_0 = 0.1; double value_1 = 0.2; From 0d69aab394693e0b861c0c44ce9f7d6775086e10 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 13 Jan 2025 20:18:50 +0000 Subject: [PATCH 164/311] a bunch of templates to draft how dragging type information through the existing operators could look like Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 168 ++++---- runtime/cudaq/dynamics/operator_sum.cpp | 200 ++++++---- runtime/cudaq/dynamics/product_operators.cpp | 116 +++--- runtime/cudaq/dynamics/scalar_operators.cpp | 35 +- runtime/cudaq/operators.h | 375 ++++++++++-------- 5 files changed, 515 insertions(+), 379 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 574891033f..fa5bc516b1 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -14,18 +14,8 @@ namespace cudaq { -/// Elementary Operator constructor. -elementary_operator::elementary_operator(std::string operator_id, - std::vector degrees) - : id(operator_id), degrees(degrees) {} -elementary_operator::elementary_operator(const elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} -elementary_operator::elementary_operator(elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} - -elementary_operator elementary_operator::identity(int degree) { +template +elementary_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -53,7 +43,8 @@ elementary_operator elementary_operator::identity(int degree) { return op; } -elementary_operator elementary_operator::zero(int degree) { +template +elementary_operator elementary_operator::zero(int degree) { std::string op_id = "zero"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -78,7 +69,8 @@ elementary_operator elementary_operator::zero(int degree) { return op; } -elementary_operator elementary_operator::annihilate(int degree) { +template +elementary_operator elementary_operator::annihilate(int degree) { std::string op_id = "annihilate"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -103,7 +95,8 @@ elementary_operator elementary_operator::annihilate(int degree) { return op; } -elementary_operator elementary_operator::create(int degree) { +template +elementary_operator elementary_operator::create(int degree) { std::string op_id = "create"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -128,7 +121,8 @@ elementary_operator elementary_operator::create(int degree) { return op; } -elementary_operator elementary_operator::position(int degree) { +template +elementary_operator elementary_operator::position(int degree) { std::string op_id = "position"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -157,7 +151,8 @@ elementary_operator elementary_operator::position(int degree) { return op; } -elementary_operator elementary_operator::momentum(int degree) { +template +elementary_operator elementary_operator::momentum(int degree) { std::string op_id = "momentum"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -186,7 +181,8 @@ elementary_operator elementary_operator::momentum(int degree) { return op; } -elementary_operator elementary_operator::number(int degree) { +template +elementary_operator elementary_operator::number(int degree) { std::string op_id = "number"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -211,7 +207,8 @@ elementary_operator elementary_operator::number(int degree) { return op; } -elementary_operator elementary_operator::parity(int degree) { +template +elementary_operator elementary_operator::parity(int degree) { std::string op_id = "parity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -236,8 +233,9 @@ elementary_operator elementary_operator::parity(int degree) { return op; } -elementary_operator -elementary_operator::displace(int degree, std::complex amplitude) { +template +elementary_operator +elementary_operator::displace(int degree, std::complex amplitude) { std::string op_id = "displace"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -274,20 +272,23 @@ elementary_operator::displace(int degree, std::complex amplitude) { return op; } -elementary_operator -elementary_operator::squeeze(int degree, std::complex amplitude) { +template +elementary_operator +elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } -matrix_2 elementary_operator::to_matrix( - const std::map dimensions, - const std::map> parameters) const { - return m_ops.at(id).generator(dimensions, parameters); +template +matrix_2 elementary_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + return m_ops[id].generator(dimensions, parameters); } /// Elementary Operator Arithmetic. -operator_sum elementary_operator::operator+(scalar_operator other) { +template +operator_sum elementary_operator::operator+(scalar_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -297,7 +298,8 @@ operator_sum elementary_operator::operator+(scalar_operator other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(scalar_operator other) { +template +operator_sum elementary_operator::operator-(scalar_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -307,13 +309,15 @@ operator_sum elementary_operator::operator-(scalar_operator other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(scalar_operator other) { +template +product_operator elementary_operator::operator*(scalar_operator other) { std::vector> _args = { *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(std::complex other) { +template +operator_sum elementary_operator::operator+(std::complex other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator(other); @@ -324,7 +328,8 @@ operator_sum elementary_operator::operator+(std::complex other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(std::complex other) { +template +operator_sum elementary_operator::operator-(std::complex other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator((-1. * other)); @@ -335,131 +340,150 @@ operator_sum elementary_operator::operator-(std::complex other) { return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(std::complex other) { +template +product_operator elementary_operator::operator*(std::complex other) { auto other_scalar = scalar_operator(other); std::vector> _args = { *this, other_scalar}; return product_operator(_args); } -operator_sum elementary_operator::operator+(double other) { +template +operator_sum elementary_operator::operator+(double other) { std::complex value(other, 0.0); return *this + value; } -operator_sum elementary_operator::operator-(double other) { +template +operator_sum elementary_operator::operator-(double other) { std::complex value(other, 0.0); return *this - value; } -product_operator elementary_operator::operator*(double other) { +template +product_operator elementary_operator::operator*(double other) { std::complex value(other, 0.0); return *this * value; } -operator_sum operator+(std::complex other, elementary_operator self) { +template +operator_sum operator+(std::complex other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _self = { + std::vector>> _self = { self}; - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), product_operator(_self)}); } -operator_sum operator-(std::complex other, elementary_operator self) { +template +operator_sum operator-(std::complex other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } -product_operator operator*(std::complex other, - elementary_operator self) { +template +product_operator operator*(std::complex other, + elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _args = { + std::vector>> _args = { other_scalar, self}; return product_operator(_args); } -operator_sum operator+(double other, elementary_operator self) { +template +operator_sum operator+(double other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _self = { + std::vector>> _self = { self}; - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), product_operator(_self)}); } -operator_sum operator-(double other, elementary_operator self) { +template +operator_sum operator-(double other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _other = { + std::vector>> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } -product_operator operator*(double other, elementary_operator self) { +template +product_operator operator*(double other, elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector> _args = { + std::vector>> _args = { other_scalar, self}; return product_operator(_args); } -product_operator elementary_operator::operator*(elementary_operator other) { - std::vector> _args = { +template +product_operator elementary_operator::operator*(elementary_operator other) { + std::vector>> _args = { *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(elementary_operator other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator+(elementary_operator other) { + std::vector>> _this = { *this}; - std::vector> _other = { + std::vector>> _other = { other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(elementary_operator other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator-(elementary_operator other) { + std::vector>> _this = { *this}; return operator_sum({product_operator(_this), (-1. * other)}); } -operator_sum elementary_operator::operator+(operator_sum other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator+(operator_sum other) { + std::vector>> _this = { *this}; - std::vector _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum + other; } -operator_sum elementary_operator::operator-(operator_sum other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator-(operator_sum other) { + std::vector>> _this = { *this}; - std::vector _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum - other; } -operator_sum elementary_operator::operator*(operator_sum other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator*(operator_sum other) { + std::vector>> _this = { *this}; - std::vector _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum * other; } -operator_sum elementary_operator::operator+(product_operator other) { - std::vector> _this = { +template +operator_sum elementary_operator::operator+(product_operator other) { + std::vector>> _this = { *this}; return operator_sum({product_operator(_this), other}); } -operator_sum elementary_operator::operator-(product_operator other) { +template +operator_sum elementary_operator::operator-(product_operator other) { return *this + (-1. * other); } -product_operator elementary_operator::operator*(product_operator other) { - std::vector> other_terms = +template +product_operator elementary_operator::operator*(product_operator other) { + std::vector>> other_terms = other.get_terms(); /// Insert this elementary operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index dd3227784d..48b2bd2f81 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -14,12 +14,8 @@ namespace cudaq { -/// Operator sum constructor given a vector of product operators. -operator_sum::operator_sum(const std::vector &terms) - : m_terms(terms) {} - // std::vector> -// operator_sum::canonicalize_product(product_operator &prod) const { +// operator_sum::canonicalize_product(product_operator &prod) const { // std::vector> // canonicalized_terms; @@ -41,7 +37,7 @@ operator_sum::operator_sum(const std::vector &terms) // if (all_degrees.size() == // std::set(all_degrees.begin(), all_degrees.end()).size()) { // std::sort(non_scalars.begin(), non_scalars.end(), -// [](const elementary_operator &a, const elementary_operator &b) { +// [](const elementary_operator &a, const elementary_operator &b) { // return a.degrees < b.degrees; // }); // } @@ -54,7 +50,7 @@ operator_sum::operator_sum(const std::vector &terms) // } // std::vector> -// operator_sum::_canonical_terms() const { +// operator_sum::_canonical_terms() const { // std::vector> terms; // // for (const auto &term : m_terms) { // // auto canonicalized = canonicalize_product(term); @@ -71,7 +67,7 @@ operator_sum::operator_sum(const std::vector &terms) // return terms; // } -// operator_sum operator_sum::canonicalize() const { +// operator_sum operator_sum::canonicalize() const { // std::vector canonical_terms; // for (const auto &term : _canonical_terms()) { // canonical_terms.push_back(product_operator(term)); @@ -79,12 +75,12 @@ operator_sum::operator_sum(const std::vector &terms) // return operator_sum(canonical_terms); // } -// bool operator_sum::operator==(const operator_sum &other) const { +// bool operator_sum::operator==(const operator_sum &other) const { // return _canonical_terms() == other._canonical_terms(); // } // // Degrees property -// std::vector operator_sum::degrees() const { +// std::vector operator_sum::degrees() const { // std::set unique_degrees; // for (const auto &term : m_terms) { // for (const auto &op : term.get_terms()) { @@ -97,7 +93,7 @@ operator_sum::operator_sum(const std::vector &terms) // } // // Parameters property -// std::map operator_sum::parameters() const { +// std::map operator_sum::parameters() const { // std::map param_map; // for (const auto &term : m_terms) { // for (const auto &op : term.get_terms()) { @@ -110,9 +106,9 @@ operator_sum::operator_sum(const std::vector &terms) // } // // Check if all terms are spin operators -// bool operator_sum::_is_spinop() const { +// bool operator_sum::_is_spinop() const { // return std::all_of( -// m_terms.begin(), m_terms.end(), [](product_operator &term) { +// m_terms.begin(), m_terms.end(), [](product_operator &term) { // return std::all_of(term.get_terms().begin(), // term.get_terms().end(), // [](const Operator &op) { return op.is_spinop(); @@ -121,31 +117,36 @@ operator_sum::operator_sum(const std::vector &terms) // } // Arithmetic operators -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector> combined_terms = m_terms; combined_terms.insert(combined_terms.end(), std::make_move_iterator(other.m_terms.begin()), std::make_move_iterator(other.m_terms.end())); return operator_sum(combined_terms); } -operator_sum operator_sum::operator-(const operator_sum &other) const { +template +operator_sum operator_sum::operator-(const operator_sum &other) const { return *this + (-1 * other); } -operator_sum operator_sum::operator-=(const operator_sum &other) { +template +operator_sum operator_sum::operator-=(const operator_sum &other) { *this = *this - other; return *this; } -operator_sum operator_sum::operator+=(const operator_sum &other) { +template +operator_sum operator_sum::operator+=(const operator_sum &other) { *this = *this + other; return *this; } -operator_sum operator_sum::operator*(operator_sum &other) const { +template +operator_sum operator_sum::operator*(operator_sum &other) const { auto self_terms = m_terms; - std::vector product_terms; + std::vector> product_terms; auto other_terms = other.get_terms(); for (auto &term : self_terms) { for (auto &other_term : other_terms) { @@ -155,218 +156,243 @@ operator_sum operator_sum::operator*(operator_sum &other) const { return operator_sum(product_terms); } -operator_sum operator_sum::operator*=(operator_sum &other) { +template +operator_sum operator_sum::operator*=(operator_sum &other) { *this = *this * other; return *this; } -operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator*(const scalar_operator &other) const { + std::vector> combined_terms = m_terms; for (auto &term : combined_terms) { term *= other; } return operator_sum(combined_terms); } -operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector combined_terms = m_terms; - std::vector> _other = { +template +operator_sum operator_sum::operator+(const scalar_operator &other) const { + std::vector> combined_terms = m_terms; + std::vector>> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } -operator_sum operator_sum::operator-(const scalar_operator &other) const { +template +operator_sum operator_sum::operator-(const scalar_operator &other) const { return *this + (-1.0 * other); } -operator_sum operator_sum::operator*=(const scalar_operator &other) { +template +operator_sum operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } -operator_sum operator_sum::operator+=(const scalar_operator &other) { +template +operator_sum operator_sum::operator+=(const scalar_operator &other) { *this = *this + other; return *this; } -operator_sum operator_sum::operator-=(const scalar_operator &other) { +template +operator_sum operator_sum::operator-=(const scalar_operator &other) { *this = *this - other; return *this; } -operator_sum operator_sum::operator*(std::complex other) const { +template +operator_sum operator_sum::operator*(std::complex other) const { return *this * scalar_operator(other); } -operator_sum operator_sum::operator+(std::complex other) const { +template +operator_sum operator_sum::operator+(std::complex other) const { return *this + scalar_operator(other); } -operator_sum operator_sum::operator-(std::complex other) const { +template +operator_sum operator_sum::operator-(std::complex other) const { return *this - scalar_operator(other); } -operator_sum operator_sum::operator*=(std::complex other) { +template +operator_sum operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); return *this; } -operator_sum operator_sum::operator+=(std::complex other) { +template +operator_sum operator_sum::operator+=(std::complex other) { *this += scalar_operator(other); return *this; } -operator_sum operator_sum::operator-=(std::complex other) { +template +operator_sum operator_sum::operator-=(std::complex other) { *this -= scalar_operator(other); return *this; } -operator_sum operator_sum::operator*(double other) const { +template +operator_sum operator_sum::operator*(double other) const { return *this * scalar_operator(other); } -operator_sum operator_sum::operator+(double other) const { +template +operator_sum operator_sum::operator+(double other) const { return *this + scalar_operator(other); } -operator_sum operator_sum::operator-(double other) const { +template +operator_sum operator_sum::operator-(double other) const { return *this - scalar_operator(other); } -operator_sum operator_sum::operator*=(double other) { +template +operator_sum operator_sum::operator*=(double other) { *this *= scalar_operator(other); return *this; } -operator_sum operator_sum::operator+=(double other) { +template +operator_sum operator_sum::operator+=(double other) { *this += scalar_operator(other); return *this; } -operator_sum operator_sum::operator-=(double other) { +template +operator_sum operator_sum::operator-=(double other) { *this -= scalar_operator(other); return *this; } -operator_sum operator*(std::complex other, operator_sum self) { +template +operator_sum operator*(std::complex other, operator_sum self) { return scalar_operator(other) * self; } -operator_sum operator+(std::complex other, operator_sum self) { +template +operator_sum operator+(std::complex other, operator_sum self) { return scalar_operator(other) + self; } -operator_sum operator-(std::complex other, operator_sum self) { +template +operator_sum operator-(std::complex other, operator_sum self) { return scalar_operator(other) - self; } -operator_sum operator*(double other, operator_sum self) { +template +operator_sum operator*(double other, operator_sum self) { return scalar_operator(other) * self; } -operator_sum operator+(double other, operator_sum self) { +template +operator_sum operator+(double other, operator_sum self) { return scalar_operator(other) + self; } -operator_sum operator-(double other, operator_sum self) { +template +operator_sum operator-(double other, operator_sum self) { return scalar_operator(other) - self; } -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector> combined_terms = m_terms; combined_terms.push_back(other); return operator_sum(combined_terms); } -operator_sum operator_sum::operator+=(const product_operator &other) { +template +operator_sum operator_sum::operator+=(const product_operator &other) { *this = *this + other; return *this; } -operator_sum operator_sum::operator-(const product_operator &other) const { +template +operator_sum operator_sum::operator-(const product_operator &other) const { return *this + (-1. * other); } -operator_sum operator_sum::operator-=(const product_operator &other) { +template +operator_sum operator_sum::operator-=(const product_operator &other) { *this = *this - other; return *this; } -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector> combined_terms = m_terms; for (auto &term : combined_terms) { term *= other; } return operator_sum(combined_terms); } -operator_sum operator_sum::operator*=(const product_operator &other) { +template +operator_sum operator_sum::operator*=(const product_operator &other) { *this = *this * other; return *this; } -operator_sum operator_sum::operator+(const elementary_operator &other) const { - std::vector combined_terms = m_terms; - std::vector> _other = { +template +operator_sum operator_sum::operator+(const elementary_operator &other) const { + std::vector> combined_terms = m_terms; + std::vector>> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } -operator_sum operator_sum::operator-(const elementary_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator-(const elementary_operator &other) const { + std::vector> combined_terms = m_terms; combined_terms.push_back((-1. * other)); return operator_sum(combined_terms); } -operator_sum operator_sum::operator*(const elementary_operator &other) const { - std::vector combined_terms = m_terms; +template +operator_sum operator_sum::operator*(const elementary_operator &other) const { + std::vector> combined_terms = m_terms; for (auto &term : combined_terms) { term *= other; } return operator_sum(combined_terms); } -operator_sum operator_sum::operator+=(const elementary_operator &other) { - std::vector> _other = { +template +operator_sum operator_sum::operator+=(const elementary_operator &other) { + std::vector>> _other = { other}; *this = *this + product_operator(_other); return *this; } -operator_sum operator_sum::operator-=(const elementary_operator &other) { - std::vector> _other = { +template +operator_sum operator_sum::operator-=(const elementary_operator &other) { + std::vector>> _other = { other}; *this = *this - product_operator(_other); return *this; } -operator_sum operator_sum::operator*=(const elementary_operator &other) { +template +operator_sum operator_sum::operator*=(const elementary_operator &other) { *this = *this * other; return *this; } -matrix_2 operator_sum::to_matrix( - const std::map &dimensions, - const std::map> ¶ms) const { - std::size_t total_dimension = 1; - for (const auto &[_, dim] : dimensions) { - total_dimension *= dim; - } - - matrix_2 result(total_dimension, total_dimension); - - for (const auto &term : m_terms) { - matrix_2 term_matrix = term.to_matrix(dimensions, params); - - result += term_matrix; - } - - return result; -} +/// FIXME: +// tensor +// operator_sum::to_matrix(const std::map &dimensions, +// const std::map ¶ms) const { +// // todo +// } -// std::string operator_sum::to_string() const { +// std::string operator_sum::to_string() const { // std::string result; // // for (const auto &term : m_terms) { // // result += term.to_string() + " + "; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 32adcaa339..5441c38bcb 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -16,12 +16,6 @@ namespace cudaq { -/// Product Operator constructors. -product_operator::product_operator( - std::vector> - atomic_operators) - : m_terms(atomic_operators) {} - // tensor kroneckerHelper(std::vector &matrices) { // // essentially we pass in the list of elementary operators to // // this function -- with lowest degree being leftmost -- then it computes @@ -83,7 +77,7 @@ matrix_2 product_operator::to_matrix( } // /// IMPLEMENT: -// tensor product_operator::to_matrix( +// tensor product_operator::to_matrix( // std::map dimensions, // std::map> parameters) { @@ -149,7 +143,8 @@ matrix_2 product_operator::to_matrix( // } // Degrees property -std::vector product_operator::degrees() const { +template +std::vector product_operator::degrees() const { std::set unique_degrees; // The variant type makes it difficult auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; @@ -166,98 +161,119 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } -operator_sum product_operator::operator+(scalar_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator+(scalar_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, product_operator(_other)}); } -operator_sum product_operator::operator-(scalar_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator-(scalar_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, -1. * product_operator(_other)}); } -product_operator product_operator::operator*(scalar_operator other) { - std::vector> +template +product_operator product_operator::operator*(scalar_operator other) { + std::vector>> combined_terms = m_terms; combined_terms.push_back(other); return product_operator(combined_terms); } -product_operator product_operator::operator*=(scalar_operator other) { +template +product_operator product_operator::operator*=(scalar_operator other) { *this = *this * other; return *this; } -operator_sum product_operator::operator+(std::complex other) { +template +operator_sum product_operator::operator+(std::complex other) { return *this + scalar_operator(other); } -operator_sum product_operator::operator-(std::complex other) { +template +operator_sum product_operator::operator-(std::complex other) { return *this - scalar_operator(other); } -product_operator product_operator::operator*(std::complex other) { +template +product_operator product_operator::operator*(std::complex other) { return *this * scalar_operator(other); } -product_operator product_operator::operator*=(std::complex other) { +template +product_operator product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } -operator_sum operator+(std::complex other, product_operator self) { +template +operator_sum operator+(std::complex other, product_operator self) { return operator_sum({scalar_operator(other), self}); } -operator_sum operator-(std::complex other, product_operator self) { +template +operator_sum operator-(std::complex other, product_operator self) { return scalar_operator(other) - self; } -product_operator operator*(std::complex other, product_operator self) { +template +product_operator operator*(std::complex other, product_operator self) { return scalar_operator(other) * self; } -operator_sum product_operator::operator+(double other) { +template +operator_sum product_operator::operator+(double other) { return *this + scalar_operator(other); } -operator_sum product_operator::operator-(double other) { +template +operator_sum product_operator::operator-(double other) { return *this - scalar_operator(other); } -product_operator product_operator::operator*(double other) { +template +product_operator product_operator::operator*(double other) { return *this * scalar_operator(other); } -product_operator product_operator::operator*=(double other) { +template +product_operator product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } -operator_sum operator+(double other, product_operator self) { +template +operator_sum operator+(double other, product_operator self) { return operator_sum({scalar_operator(other), self}); } -operator_sum operator-(double other, product_operator self) { +template +operator_sum operator-(double other, product_operator self) { return scalar_operator(other) - self; } -product_operator operator*(double other, product_operator self) { +template +product_operator operator*(double other, product_operator self) { return scalar_operator(other) * self; } -operator_sum product_operator::operator+(product_operator other) { +template +operator_sum product_operator::operator+(product_operator other) { return operator_sum({*this, other}); } -operator_sum product_operator::operator-(product_operator other) { +template +operator_sum product_operator::operator-(product_operator other) { return operator_sum({*this, (-1. * other)}); } -product_operator product_operator::operator*(product_operator other) { - std::vector> +template +product_operator product_operator::operator*(product_operator other) { + std::vector>> combined_terms = m_terms; combined_terms.insert(combined_terms.end(), std::make_move_iterator(other.m_terms.begin()), @@ -265,50 +281,58 @@ product_operator product_operator::operator*(product_operator other) { return product_operator(combined_terms); } -product_operator product_operator::operator*=(product_operator other) { +template +product_operator product_operator::operator*=(product_operator other) { *this = *this * other; return *this; } -operator_sum product_operator::operator+(elementary_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator+(elementary_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, product_operator(_other)}); } -operator_sum product_operator::operator-(elementary_operator other) { - std::vector> _other = { +template +operator_sum product_operator::operator-(elementary_operator other) { + std::vector>> _other = { other}; return operator_sum({*this, -1. * product_operator(_other)}); } -product_operator product_operator::operator*(elementary_operator other) { - std::vector> +template +product_operator product_operator::operator*(elementary_operator other) { + std::vector>> combined_terms = m_terms; combined_terms.push_back(other); return product_operator(combined_terms); } -product_operator product_operator::operator*=(elementary_operator other) { +template +product_operator product_operator::operator*=(elementary_operator other) { *this = *this * other; return *this; } -operator_sum product_operator::operator+(operator_sum other) { +template +operator_sum product_operator::operator+(operator_sum other) { std::vector other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum product_operator::operator-(operator_sum other) { +template +operator_sum product_operator::operator-(operator_sum other) { auto negative_other = (-1. * other); - std::vector other_terms = negative_other.get_terms(); + std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum product_operator::operator*(operator_sum other) { - std::vector other_terms = other.get_terms(); +template +operator_sum product_operator::operator*(operator_sum other) { + std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) { term = *this * term; } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 4d640b50b5..d8a68bb86b 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -219,54 +219,63 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); -operator_sum scalar_operator::operator+(elementary_operator other) { +template +operator_sum scalar_operator::operator+(elementary_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum({product_operator({*this}), product_operator({other})}); } -operator_sum scalar_operator::operator-(elementary_operator other) { +template +operator_sum scalar_operator::operator-(elementary_operator other) { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum( {product_operator({*this}), product_operator({-1. * other})}); } -product_operator scalar_operator::operator*(elementary_operator other) { +template +product_operator scalar_operator::operator*(elementary_operator other) { return product_operator({*this, other}); } -operator_sum scalar_operator::operator+(product_operator other) { +template +operator_sum scalar_operator::operator+(product_operator other) { return operator_sum({product_operator({*this}), other}); } -operator_sum scalar_operator::operator-(product_operator other) { +template +operator_sum scalar_operator::operator-(product_operator other) { return operator_sum({product_operator({*this}), (-1. * other)}); } -product_operator scalar_operator::operator*(product_operator other) { - std::vector> other_terms = +template +product_operator scalar_operator::operator*(product_operator other) { + std::vector>> other_terms = other.get_terms(); /// Insert this scalar operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); return product_operator(other_terms); } -operator_sum scalar_operator::operator+(operator_sum other) { - std::vector other_terms = other.get_terms(); +template +operator_sum scalar_operator::operator+(operator_sum other) { + std::vector> other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum scalar_operator::operator-(operator_sum other) { +template +operator_sum scalar_operator::operator-(operator_sum other) { auto negative_other = (-1. * other); - std::vector other_terms = negative_other.get_terms(); + std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } -operator_sum scalar_operator::operator*(operator_sum other) { - std::vector other_terms = other.get_terms(); +template +operator_sum scalar_operator::operator*(operator_sum other) { + std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) term = *this * term; return operator_sum(other_terms); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6543c5fb72..f5e4dca422 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -18,36 +18,45 @@ namespace cudaq { +template class operator_sum; + +template class product_operator; -class scalar_operator; + +template class elementary_operator; +class scalar_operator; + /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. +template // handler needs to inherit from operation_handler class operator_sum { + private: - std::vector m_terms; + std::vector> m_terms; - std::vector> - canonicalize_product(product_operator &prod) const; + std::vector>> + canonicalize_product(product_operator &prod) const; - std::vector> + std::vector>> _canonical_terms() const; public: /// @brief Empty constructor that a user can aggregate terms into. - operator_sum() = default; + // operator_sum() = default; // FIXME: NEEDED? - /// @brief Construct a `cudaq::operator_sum` given a sequence of - /// `cudaq::product_operator`'s. + /// @brief Construct a `cudaq::operator_sum` given a sequence of + /// `cudaq::product_operator`'s. /// This operator expression represents a sum of terms, where each term /// is a product of elementary and scalar operators. - operator_sum(const std::vector &terms); + operator_sum(const std::vector> &terms) + : m_terms(terms) {} - operator_sum canonicalize() const; + operator_sum canonicalize() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. @@ -59,7 +68,7 @@ class operator_sum { // template // TEval _evaluate(OperatorArithmetics &arithmetics) const; - /// @brief Return the `operator_sum` as a matrix. + /// @brief Return the `operator_sum` as a matrix. /// @arg `dimensions` : A mapping that specifies the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -71,50 +80,50 @@ class operator_sum { const std::map> ¶ms = {}) const; // Arithmetic operators - operator_sum operator+(const operator_sum &other) const; - operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(operator_sum &other) const; - operator_sum operator*=(operator_sum &other); - operator_sum operator+=(const operator_sum &other); - operator_sum operator-=(const operator_sum &other); - operator_sum operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*=(const scalar_operator &other); - operator_sum operator+=(const scalar_operator &other); - operator_sum operator-=(const scalar_operator &other); - operator_sum operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - operator_sum operator*=(std::complex other); - operator_sum operator+=(std::complex other); - operator_sum operator-=(std::complex other); - operator_sum operator*(double other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - operator_sum operator*=(double other); - operator_sum operator+=(double other); - operator_sum operator-=(double other); - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; - operator_sum operator*=(const product_operator &other); - operator_sum operator+=(const product_operator &other); - operator_sum operator-=(const product_operator &other); - operator_sum operator+(const elementary_operator &other) const; - operator_sum operator-(const elementary_operator &other) const; - operator_sum operator*(const elementary_operator &other) const; - operator_sum operator*=(const elementary_operator &other); - operator_sum operator+=(const elementary_operator &other); - operator_sum operator-=(const elementary_operator &other); - - /// @brief Return the operator_sum as a string. + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; + operator_sum operator*(operator_sum &other) const; + operator_sum operator*=(operator_sum &other); + operator_sum operator+=(const operator_sum &other); + operator_sum operator-=(const operator_sum &other); + operator_sum operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + operator_sum operator*=(const scalar_operator &other); + operator_sum operator+=(const scalar_operator &other); + operator_sum operator-=(const scalar_operator &other); + operator_sum operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + operator_sum operator*=(std::complex other); + operator_sum operator+=(std::complex other); + operator_sum operator-=(std::complex other); + operator_sum operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + operator_sum operator*=(double other); + operator_sum operator+=(double other); + operator_sum operator-=(double other); + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + operator_sum operator*=(const product_operator &other); + operator_sum operator+=(const product_operator &other); + operator_sum operator-=(const product_operator &other); + operator_sum operator+(const elementary_operator &other) const; + operator_sum operator-(const elementary_operator &other) const; + operator_sum operator*(const elementary_operator &other) const; + operator_sum operator*=(const elementary_operator &other); + operator_sum operator+=(const elementary_operator &other); + operator_sum operator-=(const elementary_operator &other); + + /// @brief Return the operator_sum as a string. std::string to_string() const; /// @brief Return the number of operator terms that make up this operator sum. int term_count() const { return m_terms.size(); } - /// @brief True, if the other value is an operator_sum with equivalent terms, + /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. @@ -123,29 +132,38 @@ class operator_sum { /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. - bool operator==(const operator_sum &other) const; + bool operator==(const operator_sum &other) const; /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector get_terms() const { return m_terms; } + std::vector> get_terms() { return m_terms; } }; -operator_sum operator*(std::complex other, operator_sum self); -operator_sum operator+(std::complex other, operator_sum self); -operator_sum operator-(std::complex other, operator_sum self); -operator_sum operator*(double other, operator_sum self); -operator_sum operator+(double other, operator_sum self); -operator_sum operator-(double other, operator_sum self); + +template +operator_sum operator*(std::complex other, operator_sum self); +template +operator_sum operator+(std::complex other, operator_sum self); +template +operator_sum operator-(std::complex other, operator_sum self); +template +operator_sum operator*(double other, operator_sum self); +template +operator_sum operator+(double other, operator_sum self); +template +operator_sum operator-(double other, operator_sum self); /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types /// that can. -class product_operator : public operator_sum { +template // handler needs to inherit from operation_handler +class product_operator : public operator_sum { + private: - std::vector> m_terms; + std::vector>> m_terms; public: - product_operator() = default; + // product_operator() = default; // FIXME: needed? ~product_operator() = default; // Constructor for an operator expression that represents a product @@ -153,35 +171,36 @@ class product_operator : public operator_sum { // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. product_operator( - std::vector> - atomic_operators); + std::vector>> + atomic_operators) + : m_terms(atomic_operators) {} // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - product_operator operator*=(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); - product_operator operator*=(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); - product_operator operator*=(scalar_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - product_operator operator*=(product_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - product_operator operator*=(elementary_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator*(operator_sum other); - - /// @brief True, if the other value is an operator_sum with equivalent terms, + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + product_operator operator*=(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + product_operator operator*=(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + product_operator operator*=(scalar_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + product_operator operator*=(product_operator other); + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + product_operator operator*=(elementary_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator*(operator_sum other); + + /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. @@ -190,12 +209,12 @@ class product_operator : public operator_sum { /// evaluate to False, even if two operators in reality are the same. /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. - bool operator==(product_operator other); + bool operator==(product_operator other); - /// @brief Return the `product_operator` as a string. + /// @brief Return the `product_operator` as a string. std::string to_string() const; - /// @brief Return the `operator_sum` as a matrix. + /// @brief Return the `operator_sum` as a matrix. /// @arg `dimensions` : A mapping that specifies the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -220,65 +239,82 @@ class product_operator : public operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> - get_terms() const { + std::vector>> get_terms() { return m_terms; }; }; -operator_sum operator+(std::complex other, product_operator self); -operator_sum operator-(std::complex other, product_operator self); -product_operator operator*(std::complex other, product_operator self); -operator_sum operator+(double other, product_operator self); -operator_sum operator-(double other, product_operator self); -product_operator operator*(double other, product_operator self); - -class elementary_operator : public product_operator { + +template +operator_sum operator+(std::complex other, product_operator self); +template +operator_sum operator-(std::complex other, product_operator self); +template +product_operator operator*(std::complex other, product_operator self); +template +operator_sum operator+(double other, product_operator self); +template +operator_sum operator-(double other, product_operator self); +template +product_operator operator*(double other, product_operator self); + +template +class elementary_operator : public product_operator { + private: std::map m_ops; public: // The constructor should never be called directly by the user: - // Keeping it internally documentd for now, however. + // Keeping it internally documented for now, however. /// @brief Constructor. /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - elementary_operator(std::string operator_id, std::vector degrees); + elementary_operator(std::string operator_id, std::vector degrees) + : id(operator_id), degrees(degrees) {} - // Copy constructor. - elementary_operator(const elementary_operator &other); - elementary_operator(elementary_operator &other); + // Copy constructor. FIXME: NEEDED? + elementary_operator(const elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} + + elementary_operator(elementary_operator &other) + : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), + degrees(other.degrees), id(other.id) {} // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator+=(operator_sum other); - operator_sum operator-=(operator_sum other); - operator_sum operator*(operator_sum other); + operator_sum operator+(std::complex other); + operator_sum operator-(std::complex other); + product_operator operator*(std::complex other); + operator_sum operator+(double other); + operator_sum operator-(double other); + product_operator operator*(double other); + operator_sum operator+(scalar_operator other); + operator_sum operator-(scalar_operator other); + product_operator operator*(scalar_operator other); + // big question regarding templates is whether coefficients are part of product or are part of elem ops - + // part of product seems more intuitive to me, and probably simplifies code; + // that would mean even for eager prod/add, we still get a prod/sum as result + operator_sum operator+(elementary_operator other); + operator_sum operator-(elementary_operator other); + product_operator operator*(elementary_operator other); + operator_sum operator+(product_operator other); + operator_sum operator-(product_operator other); + product_operator operator*(product_operator other); + operator_sum operator+(operator_sum other); + operator_sum operator-(operator_sum other); + operator_sum operator+=(operator_sum other); + operator_sum operator-=(operator_sum other); + operator_sum operator*(operator_sum other); /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + bool operator==(elementary_operator other); - /// @brief Return the `elementary_operator` as a string. + /// @brief Return the `elementary_operator` as a string. std::string to_string() const; - /// @brief Return the `elementary_operator` as a matrix. + /// @brief Return the `elementary_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -288,18 +324,18 @@ class elementary_operator : public product_operator { const std::map> parameters) const; // Predefined operators. - static elementary_operator identity(int degree); - static elementary_operator zero(int degree); - static elementary_operator annihilate(int degree); - static elementary_operator create(int degree); - static elementary_operator momentum(int degree); - static elementary_operator number(int degree); - static elementary_operator parity(int degree); - static elementary_operator position(int degree); + static elementary_operator identity(int degree); + static elementary_operator zero(int degree); + static elementary_operator annihilate(int degree); + static elementary_operator create(int degree); + static elementary_operator momentum(int degree); + static elementary_operator number(int degree); + static elementary_operator parity(int degree); + static elementary_operator position(int degree); /// FIXME: - static elementary_operator squeeze(int degree, + static elementary_operator squeeze(int degree, std::complex amplitude); - static elementary_operator displace(int degree, + static elementary_operator displace(int degree, std::complex amplitude); /// @brief Adds the definition of an elementary operator with the given id to @@ -357,15 +393,24 @@ class elementary_operator : public product_operator { // pauli_word to_pauli_word ovveride(); }; // Reverse order arithmetic for elementary operators against pure scalars. -operator_sum operator+(std::complex other, elementary_operator self); -operator_sum operator-(std::complex other, elementary_operator self); -product_operator operator*(std::complex other, - elementary_operator self); -operator_sum operator+(double other, elementary_operator self); -operator_sum operator-(double other, elementary_operator self); -product_operator operator*(double other, elementary_operator self); - -class scalar_operator : public product_operator { +template +operator_sum operator+(std::complex other, elementary_operator self); +template +operator_sum operator-(std::complex other, elementary_operator self); +template +product_operator operator*(std::complex other, + elementary_operator self); +template +operator_sum operator+(double other, elementary_operator self); +template +operator_sum operator-(double other, elementary_operator self); +template +product_operator operator*(double other, elementary_operator self); + +// FIXME: check if we really need the inheritance from prod operator, and if so what it should be +// (check if this really should be its own class?) +class scalar_operator { + private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. @@ -392,25 +437,33 @@ class scalar_operator : public product_operator { scalar_operator operator/(scalar_operator other); /// TODO: implement and test pow scalar_operator pow(scalar_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator*(operator_sum other); + template + operator_sum operator+(elementary_operator other); + template + operator_sum operator-(elementary_operator other); + template + product_operator operator*(elementary_operator other); + template + operator_sum operator+(product_operator other); + template + operator_sum operator-(product_operator other); + template + product_operator operator*(product_operator other); + template + operator_sum operator+(operator_sum other); + template + operator_sum operator-(operator_sum other); + template + operator_sum operator*(operator_sum other); /// @brief Return the scalar operator as a concrete complex value. std::complex evaluate(std::map> parameters) const; // Return the scalar operator as a 1x1 matrix. This is needed for - // compatability with the other inherited classes. - matrix_2 - to_matrix(const std::map dimensions, - const std::map> parameters) const; + // compatibility with the other inherited classes. + matrix_2 to_matrix(std::map dimensions, + std::map> parameters); // /// @brief Returns true if other is a scalar operator with the same // /// generator. From 265e3b306c58b52601da2c79b283a4b6b7344aeb Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 19:34:05 +0000 Subject: [PATCH 165/311] wip to use elementary_operator as matrix handler Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 149 +++--- runtime/cudaq/dynamics/operator_sum.cpp | 80 ++-- runtime/cudaq/dynamics/product_operators.cpp | 108 ++--- runtime/cudaq/dynamics/scalar_operators.cpp | 242 ++++------ runtime/cudaq/operators.h | 449 +++++++++--------- 5 files changed, 481 insertions(+), 547 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index fa5bc516b1..baa5df8396 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -10,12 +10,12 @@ #include "cudaq/operators.h" #include +#include #include namespace cudaq { -template -elementary_operator elementary_operator::identity(int degree) { +elementary_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -43,8 +43,7 @@ elementary_operator elementary_operator::identity(int degr return op; } -template -elementary_operator elementary_operator::zero(int degree) { +elementary_operator elementary_operator::zero(int degree) { std::string op_id = "zero"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -69,8 +68,7 @@ elementary_operator elementary_operator::zero(int degree) return op; } -template -elementary_operator elementary_operator::annihilate(int degree) { +elementary_operator elementary_operator::annihilate(int degree) { std::string op_id = "annihilate"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -95,8 +93,7 @@ elementary_operator elementary_operator::annihilate(int de return op; } -template -elementary_operator elementary_operator::create(int degree) { +elementary_operator elementary_operator::create(int degree) { std::string op_id = "create"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -121,8 +118,7 @@ elementary_operator elementary_operator::create(int degree return op; } -template -elementary_operator elementary_operator::position(int degree) { +elementary_operator elementary_operator::position(int degree) { std::string op_id = "position"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -151,8 +147,7 @@ elementary_operator elementary_operator::position(int degr return op; } -template -elementary_operator elementary_operator::momentum(int degree) { +elementary_operator elementary_operator::momentum(int degree) { std::string op_id = "momentum"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -181,8 +176,7 @@ elementary_operator elementary_operator::momentum(int degr return op; } -template -elementary_operator elementary_operator::number(int degree) { +elementary_operator elementary_operator::number(int degree) { std::string op_id = "number"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -207,8 +201,7 @@ elementary_operator elementary_operator::number(int degree return op; } -template -elementary_operator elementary_operator::parity(int degree) { +elementary_operator elementary_operator::parity(int degree) { std::string op_id = "parity"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -233,9 +226,8 @@ elementary_operator elementary_operator::parity(int degree return op; } -template -elementary_operator -elementary_operator::displace(int degree, std::complex amplitude) { +elementary_operator +elementary_operator::displace(int degree, std::complex amplitude) { std::string op_id = "displace"; std::vector degrees = {degree}; auto op = elementary_operator(op_id, degrees); @@ -272,14 +264,12 @@ elementary_operator::displace(int degree, std::complex amplit return op; } -template -elementary_operator -elementary_operator::squeeze(int degree, std::complex amplitude) { +elementary_operator +elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } -template -matrix_2 elementary_operator::to_matrix( +matrix_2 elementary_operator::to_matrix( std::map dimensions, std::map> parameters) { return m_ops[id].generator(dimensions, parameters); @@ -287,8 +277,7 @@ matrix_2 elementary_operator::to_matrix( /// Elementary Operator Arithmetic. -template -operator_sum elementary_operator::operator+(scalar_operator other) { +operator_sum elementary_operator::operator+(const scalar_operator other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -298,8 +287,7 @@ operator_sum elementary_operator::operator+(scalar_operato return operator_sum({product_operator(_this), product_operator(_other)}); } -template -operator_sum elementary_operator::operator-(scalar_operator other) { +operator_sum elementary_operator::operator-(const scalar_operator other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. std::vector> _this = { @@ -309,15 +297,13 @@ operator_sum elementary_operator::operator-(scalar_operato return operator_sum({product_operator(_this), product_operator(_other)}); } -template -product_operator elementary_operator::operator*(scalar_operator other) { +product_operator elementary_operator::operator*(const scalar_operator other) const { std::vector> _args = { *this, other}; return product_operator(_args); } -template -operator_sum elementary_operator::operator+(std::complex other) { +operator_sum elementary_operator::operator+(const std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator(other); @@ -328,8 +314,7 @@ operator_sum elementary_operator::operator+(std::complex -operator_sum elementary_operator::operator-(std::complex other) { +operator_sum elementary_operator::operator-(const std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. auto other_scalar = scalar_operator((-1. * other)); @@ -340,150 +325,136 @@ operator_sum elementary_operator::operator-(std::complex -product_operator elementary_operator::operator*(std::complex other) { +product_operator elementary_operator::operator*(const std::complex other) const { auto other_scalar = scalar_operator(other); std::vector> _args = { *this, other_scalar}; return product_operator(_args); } -template -operator_sum elementary_operator::operator+(double other) { +operator_sum elementary_operator::operator+(double other) const { std::complex value(other, 0.0); return *this + value; } -template -operator_sum elementary_operator::operator-(double other) { +operator_sum elementary_operator::operator-(double other) const { std::complex value(other, 0.0); return *this - value; } -template -product_operator elementary_operator::operator*(double other) { +product_operator elementary_operator::operator*(double other) const { std::complex value(other, 0.0); return *this * value; } -template -operator_sum operator+(std::complex other, elementary_operator self) { +operator_sum operator+(const std::complex other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _self = { + std::vector> _self = { self}; - std::vector>> _other = { + std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum({product_operator(_other), product_operator(_self)}); } template -operator_sum operator-(std::complex other, elementary_operator self) { +operator_sum operator-(const std::complex other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _other = { + std::vector> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } template -product_operator operator*(std::complex other, - elementary_operator self) { +product_operator operator*(const std::complex other, + const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _args = { + std::vector> _args = { other_scalar, self}; return product_operator(_args); } template -operator_sum operator+(double other, elementary_operator self) { +operator_sum operator+(double other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _self = { + std::vector> _self = { self}; - std::vector>> _other = { + std::vector> _other = { other_scalar}; return operator_sum({product_operator(_other), product_operator(_self)}); } template -operator_sum operator-(double other, elementary_operator self) { +operator_sum operator-(double other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _other = { + std::vector> _other = { other_scalar}; return operator_sum({product_operator(_other), (-1. * self)}); } template -product_operator operator*(double other, elementary_operator self) { +product_operator operator*(double other, const elementary_operator self) { auto other_scalar = scalar_operator(other); - std::vector>> _args = { + std::vector> _args = { other_scalar, self}; return product_operator(_args); } -template -product_operator elementary_operator::operator*(elementary_operator other) { - std::vector>> _args = { +product_operator elementary_operator::operator*(const elementary_operator other) const { + std::vector> _args = { *this, other}; return product_operator(_args); } -template -operator_sum elementary_operator::operator+(elementary_operator other) { - std::vector>> _this = { +operator_sum elementary_operator::operator+(const elementary_operator other) const { + std::vector> _this = { *this}; - std::vector>> _other = { + std::vector> _other = { other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -template -operator_sum elementary_operator::operator-(elementary_operator other) { - std::vector>> _this = { +operator_sum elementary_operator::operator-(const elementary_operator other) const { + std::vector> _this = { *this}; return operator_sum({product_operator(_this), (-1. * other)}); } -template -operator_sum elementary_operator::operator+(operator_sum other) { - std::vector>> _this = { +operator_sum elementary_operator::operator+(const operator_sum other) const { + std::vector> _this = { *this}; - std::vector> _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum + other; } -template -operator_sum elementary_operator::operator-(operator_sum other) { - std::vector>> _this = { +operator_sum elementary_operator::operator-(const operator_sum other) const { + std::vector> _this = { *this}; - std::vector> _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum - other; } -template -operator_sum elementary_operator::operator*(operator_sum other) { - std::vector>> _this = { +operator_sum elementary_operator::operator*(const operator_sum other) const { + std::vector> _this = { *this}; - std::vector> _prods = {product_operator(_this)}; + std::vector> _prods = {product_operator(_this)}; auto selfOpSum = operator_sum(_prods); return selfOpSum * other; } -template -operator_sum elementary_operator::operator+(product_operator other) { - std::vector>> _this = { +operator_sum elementary_operator::operator+(const product_operator other) const { + std::vector> _this = { *this}; return operator_sum({product_operator(_this), other}); } -template -operator_sum elementary_operator::operator-(product_operator other) { +operator_sum elementary_operator::operator-(const product_operator other) const { return *this + (-1. * other); } -template -product_operator elementary_operator::operator*(product_operator other) { - std::vector>> other_terms = +product_operator elementary_operator::operator*(const product_operator other) const { + std::vector> other_terms = other.get_terms(); /// Insert this elementary operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 48b2bd2f81..e3a3e97e45 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -14,30 +14,30 @@ namespace cudaq { -// std::vector> +// std::vector> // operator_sum::canonicalize_product(product_operator &prod) const { -// std::vector> +// std::vector> // canonicalized_terms; // std::vector all_degrees; // std::vector scalars; -// std::vector non_scalars; +// std::vector non_scalars; // for (const auto &op : prod.get_terms()) { // if (std::holds_alternative(op)) { // scalars.push_back(*std::get(op)); // } else { -// non_scalars.push_back(*std::get(op)); +// non_scalars.push_back(*std::get(op)); // all_degrees.insert(all_degrees.end(), -// std::get(op).degrees.begin(), -// std::get(op).degrees.end()); +// std::get(op).degrees.begin(), +// std::get(op).degrees.end()); // } // } // if (all_degrees.size() == // std::set(all_degrees.begin(), all_degrees.end()).size()) { // std::sort(non_scalars.begin(), non_scalars.end(), -// [](const elementary_operator &a, const elementary_operator &b) { +// [](const HandlerTy &a, const HandlerTy &b) { // return a.degrees < b.degrees; // }); // } @@ -49,10 +49,10 @@ namespace cudaq { // return canonicalized_terms; // } -// std::vector> +// std::vector> // operator_sum::_canonical_terms() const { -// std::vector> terms; -// // for (const auto &term : m_terms) { +// std::vector> terms; +// // for (const auto &term : terms) { // // auto canonicalized = canonicalize_product(term); // // terms.insert(terms.end(), canonicalized.begin(), canonicalized.end()); // // } @@ -82,7 +82,7 @@ namespace cudaq { // // Degrees property // std::vector operator_sum::degrees() const { // std::set unique_degrees; -// for (const auto &term : m_terms) { +// for (const auto &term : terms) { // for (const auto &op : term.get_terms()) { // unique_degrees.insert(op.get_degrees().begin(), // op.get_degrees().end()); @@ -95,7 +95,7 @@ namespace cudaq { // // Parameters property // std::map operator_sum::parameters() const { // std::map param_map; -// for (const auto &term : m_terms) { +// for (const auto &term : terms) { // for (const auto &op : term.get_terms()) { // auto op_params = op.parameters(); // param_map.insert(op_params.begin(), op.params.end()); @@ -108,7 +108,7 @@ namespace cudaq { // // Check if all terms are spin operators // bool operator_sum::_is_spinop() const { // return std::all_of( -// m_terms.begin(), m_terms.end(), [](product_operator &term) { +// terms.begin(), terms.end(), [](product_operator &term) { // return std::all_of(term.get_terms().begin(), // term.get_terms().end(), // [](const Operator &op) { return op.is_spinop(); @@ -116,13 +116,15 @@ namespace cudaq { // }); // } +template class operator_sum; + // Arithmetic operators template operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.m_terms.begin()), - std::make_move_iterator(other.m_terms.end())); + std::make_move_iterator(other.terms.begin()), + std::make_move_iterator(other.terms.end())); return operator_sum(combined_terms); } @@ -131,6 +133,10 @@ operator_sum operator_sum::operator-(const operator_sum operator_sum::operator-( +// const operator_sum &other) const; + template operator_sum operator_sum::operator-=(const operator_sum &other) { *this = *this - other; @@ -144,8 +150,8 @@ operator_sum operator_sum::operator+=(const operator_sum -operator_sum operator_sum::operator*(operator_sum &other) const { - auto self_terms = m_terms; +operator_sum operator_sum::operator*(const operator_sum &other) const { + auto self_terms = terms; std::vector> product_terms; auto other_terms = other.get_terms(); for (auto &term : self_terms) { @@ -157,14 +163,14 @@ operator_sum operator_sum::operator*(operator_sum -operator_sum operator_sum::operator*=(operator_sum &other) { +operator_sum operator_sum::operator*=(const operator_sum &other) { *this = *this * other; return *this; } template operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; for (auto &term : combined_terms) { term *= other; } @@ -173,8 +179,8 @@ operator_sum operator_sum::operator*(const scalar_operator template operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = m_terms; - std::vector>> _other = { + std::vector> combined_terms = terms; + std::vector> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); @@ -301,7 +307,7 @@ operator_sum operator-(double other, operator_sum self) { template operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; combined_terms.push_back(other); return operator_sum(combined_terms); } @@ -325,7 +331,7 @@ operator_sum operator_sum::operator-=(const product_operat template operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = m_terms; + std::vector> combined_terms = terms; for (auto &term : combined_terms) { term *= other; } @@ -339,24 +345,24 @@ operator_sum operator_sum::operator*=(const product_operat } template -operator_sum operator_sum::operator+(const elementary_operator &other) const { - std::vector> combined_terms = m_terms; - std::vector>> _other = { +operator_sum operator_sum::operator+(const HandlerTy &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } template -operator_sum operator_sum::operator-(const elementary_operator &other) const { - std::vector> combined_terms = m_terms; +operator_sum operator_sum::operator-(const HandlerTy &other) const { + std::vector> combined_terms = terms; combined_terms.push_back((-1. * other)); return operator_sum(combined_terms); } template -operator_sum operator_sum::operator*(const elementary_operator &other) const { - std::vector> combined_terms = m_terms; +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> combined_terms = terms; for (auto &term : combined_terms) { term *= other; } @@ -364,23 +370,23 @@ operator_sum operator_sum::operator*(const elementary_oper } template -operator_sum operator_sum::operator+=(const elementary_operator &other) { - std::vector>> _other = { +operator_sum operator_sum::operator+=(const HandlerTy &other) { + std::vector> _other = { other}; *this = *this + product_operator(_other); return *this; } template -operator_sum operator_sum::operator-=(const elementary_operator &other) { - std::vector>> _other = { +operator_sum operator_sum::operator-=(const HandlerTy &other) { + std::vector> _other = { other}; *this = *this - product_operator(_other); return *this; } template -operator_sum operator_sum::operator*=(const elementary_operator &other) { +operator_sum operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } @@ -394,7 +400,7 @@ operator_sum operator_sum::operator*=(const elementary_ope // std::string operator_sum::to_string() const { // std::string result; -// // for (const auto &term : m_terms) { +// // for (const auto &term : terms) { // // result += term.to_string() + " + "; // // } // // // Remove last " + " diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 5441c38bcb..293fb2cfc1 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -100,7 +100,7 @@ matrix_2 product_operator::to_matrix( // return outMatrix; // }; // std::vector matricesFullVectorSpace; -// for (auto &term : m_terms) { +// for (auto &term : ops) { // auto op_degrees = std::visit(getDegrees, term); // std::cout << "here 58\n"; // // Keeps track of if we've already inserted the operator matrix @@ -142,6 +142,8 @@ matrix_2 product_operator::to_matrix( // return out; // } +template class product_operator; + // Degrees property template std::vector product_operator::degrees() const { @@ -149,7 +151,7 @@ std::vector product_operator::degrees() const { // The variant type makes it difficult auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; auto endFunc = [](auto &&t) { return t.degrees.end(); }; - for (const auto &term : m_terms) { + for (const auto &term : ops) { unique_degrees.insert(std::visit(beginFunc, term), std::visit(endFunc, term)); } @@ -162,81 +164,81 @@ std::vector product_operator::degrees() const { } template -operator_sum product_operator::operator+(scalar_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator+(const scalar_operator other) const { + std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum({*this, product_operator(_other)}); } template -operator_sum product_operator::operator-(scalar_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator-(const scalar_operator other) const { + std::vector> _other = { other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, -1. * product_operator(_other)}); } template -product_operator product_operator::operator*(scalar_operator other) { - std::vector>> - combined_terms = m_terms; +product_operator product_operator::operator*(const scalar_operator other) const { + std::vector> + combined_terms = ops; combined_terms.push_back(other); return product_operator(combined_terms); } template -product_operator product_operator::operator*=(scalar_operator other) { +product_operator product_operator::operator*=(const scalar_operator other) { *this = *this * other; return *this; } template -operator_sum product_operator::operator+(std::complex other) { +operator_sum product_operator::operator+(const std::complex other) const { return *this + scalar_operator(other); } template -operator_sum product_operator::operator-(std::complex other) { +operator_sum product_operator::operator-(const std::complex other) const { return *this - scalar_operator(other); } template -product_operator product_operator::operator*(std::complex other) { +product_operator product_operator::operator*(const std::complex other) const { return *this * scalar_operator(other); } template -product_operator product_operator::operator*=(std::complex other) { +product_operator product_operator::operator*=(const std::complex other) { *this = *this * scalar_operator(other); return *this; } template -operator_sum operator+(std::complex other, product_operator self) { - return operator_sum({scalar_operator(other), self}); +operator_sum operator+(const std::complex other, const product_operator self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum operator-(std::complex other, product_operator self) { +operator_sum operator-(const std::complex other, const product_operator self) { return scalar_operator(other) - self; } template -product_operator operator*(std::complex other, product_operator self) { +product_operator operator*(const std::complex other, const product_operator self) { return scalar_operator(other) * self; } template -operator_sum product_operator::operator+(double other) { +operator_sum product_operator::operator+(double other) const { return *this + scalar_operator(other); } template -operator_sum product_operator::operator-(double other) { +operator_sum product_operator::operator-(double other) const { return *this - scalar_operator(other); } template -product_operator product_operator::operator*(double other) { +product_operator product_operator::operator*(double other) const { return *this * scalar_operator(other); } @@ -247,96 +249,96 @@ product_operator product_operator::operator*=(double other } template -operator_sum operator+(double other, product_operator self) { - return operator_sum({scalar_operator(other), self}); +operator_sum operator+(double other, const product_operator self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum operator-(double other, product_operator self) { +operator_sum operator-(double other, const product_operator self) { return scalar_operator(other) - self; } template -product_operator operator*(double other, product_operator self) { +product_operator operator*(double other, const product_operator self) { return scalar_operator(other) * self; } template -operator_sum product_operator::operator+(product_operator other) { - return operator_sum({*this, other}); +operator_sum product_operator::operator+(const product_operator other) const { + return operator_sum({*this, other}); } template -operator_sum product_operator::operator-(product_operator other) { - return operator_sum({*this, (-1. * other)}); +operator_sum product_operator::operator-(const product_operator other) const { + return operator_sum({*this, (-1. * other)}); } template -product_operator product_operator::operator*(product_operator other) { - std::vector>> - combined_terms = m_terms; +product_operator product_operator::operator*(const product_operator other) const { + std::vector> + combined_terms = ops; combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.m_terms.begin()), - std::make_move_iterator(other.m_terms.end())); + std::make_move_iterator(other.ops.begin()), + std::make_move_iterator(other.ops.end())); return product_operator(combined_terms); } template -product_operator product_operator::operator*=(product_operator other) { +product_operator product_operator::operator*=(const product_operator other) { *this = *this * other; return *this; } template -operator_sum product_operator::operator+(elementary_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator+(const HandlerTy other) const { + std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum({*this, product_operator(_other)}); } template -operator_sum product_operator::operator-(elementary_operator other) { - std::vector>> _other = { +operator_sum product_operator::operator-(const HandlerTy other) const { + std::vector> _other = { other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, -1. * product_operator(_other)}); } template -product_operator product_operator::operator*(elementary_operator other) { - std::vector>> - combined_terms = m_terms; +product_operator product_operator::operator*(const HandlerTy other) const { + std::vector> + combined_terms = ops; combined_terms.push_back(other); return product_operator(combined_terms); } template -product_operator product_operator::operator*=(elementary_operator other) { +product_operator product_operator::operator*=(const HandlerTy other) { *this = *this * other; return *this; } template -operator_sum product_operator::operator+(operator_sum other) { +operator_sum product_operator::operator+(const operator_sum other) const { std::vector other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); + return operator_sum(other_terms); } template -operator_sum product_operator::operator-(operator_sum other) { +operator_sum product_operator::operator-(const operator_sum other) const { auto negative_other = (-1. * other); std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); + return operator_sum(other_terms); } template -operator_sum product_operator::operator*(operator_sum other) { +operator_sum product_operator::operator*(const operator_sum other) const { std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) { term = *this * term; } - return operator_sum(other_terms); + return operator_sum(other_terms); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index d8a68bb86b..a31a1257e5 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,12 +14,6 @@ namespace cudaq { -/// Constructors. -scalar_operator::scalar_operator(const scalar_operator &other) - : generator(other.generator), m_constant_value(other.m_constant_value) {} -scalar_operator::scalar_operator(scalar_operator &other) - : generator(other.generator), m_constant_value(other.m_constant_value) {} - /// @brief Constructor that just takes and returns a complex double value. scalar_operator::scalar_operator(std::complex value) { m_constant_value = value; @@ -40,7 +34,7 @@ scalar_operator::scalar_operator(double value) { } std::complex scalar_operator::evaluate( - std::map> parameters) const { + const std::map> parameters) const { return generator(parameters); } @@ -52,182 +46,146 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator operator op(const scalar_operator &self, \ + const scalar_operator other) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return self \ + .evaluate(parameters) op other \ + .evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_SCALAR_OPS(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS(-); +ARITHMETIC_OPERATIONS_SCALAR_OPS(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS(/); + #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator operator op(std::complex other, \ - scalar_operator self) { \ - /* Create an operator for the complex double value. */ \ - auto otherOperator = scalar_operator(other); \ - /* Create an operator that we will store the result in and return to the \ - * user. */ \ - scalar_operator returnOperator; \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - returnOperator._operators_to_compose.push_back(self); \ - returnOperator._operators_to_compose.push_back(otherOperator); \ + scalar_operator operator op(const scalar_operator &self, \ + const std::complex other) { \ auto newGenerator = \ [&](std::map> parameters) { \ - /* FIXME: I have to use this hacky `.get_val()` on the newly created \ - * operator for the given complex double -- because calling the \ - * evaluate function returns 0.0 . I have no clue why??? */ \ - return returnOperator._operators_to_compose[0] \ - .evaluate(parameters) op returnOperator._operators_to_compose[1] \ - .get_val(); \ + return self.evaluate(parameters) op other; \ }; \ - returnOperator.generator = ScalarCallbackFunction(newGenerator); \ - return returnOperator; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(scalar_operator self, \ - std::complex other) { \ - /* Create an operator for the complex double value. */ \ - auto otherOperator = scalar_operator(other); \ - /* Create an operator that we will store the result in and return to the \ - * user. */ \ - scalar_operator returnOperator; \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - returnOperator._operators_to_compose.push_back(self); \ - returnOperator._operators_to_compose.push_back(otherOperator); \ +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); + +#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ + scalar_operator operator op(const scalar_operator &self, double other) { \ auto newGenerator = \ [&](std::map> parameters) { \ - /* FIXME: I have to use this hacky `.get_val()` on the newly created \ - * operator for the given complex double -- because calling the \ - * evaluate function returns 0.0 . I have no clue why??? */ \ - return returnOperator._operators_to_compose[1] \ - .get_val() op returnOperator._operators_to_compose[0] \ + return self \ + .evaluate(parameters) op other; \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_DOUBLES(+); +ARITHMETIC_OPERATIONS_DOUBLES(-); +ARITHMETIC_OPERATIONS_DOUBLES(*); +ARITHMETIC_OPERATIONS_DOUBLES(/); + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, const scalar_operator other) { \ + /* Need to move the existing generating function to a new operator so \ + * that we can modify the generator in `self` in-place. */ \ + scalar_operator prevSelf(self); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return prevSelf \ + .evaluate(parameters) op other \ .evaluate(parameters); \ }; \ - returnOperator.generator = ScalarCallbackFunction(newGenerator); \ - return returnOperator; \ + self.generator = ScalarCallbackFunction(newGenerator); \ } +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); + #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, std::complex other) { \ - /* Create an operator for the complex double value. */ \ - auto otherOperator = scalar_operator(other); \ + void operator op(scalar_operator &self, const std::complex other) { \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in `self` in-place. */ \ - scalar_operator copy(self); \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - self._operators_to_compose.push_back(copy); \ - self._operators_to_compose.push_back(otherOperator); \ + scalar_operator prevSelf(self); \ auto newGenerator = \ [&](std::map> parameters) { \ - /* FIXME: I have to use this hacky `.get_val()` on the newly created \ - * operator for the given complex double -- because calling the \ - * evaluate function returns 0.0 . I have no clue why??? */ \ - return self._operators_to_compose[0] \ - .evaluate(parameters) op self._operators_to_compose[1] \ - .get_val(); \ + return prevSelf \ + .evaluate(parameters) op other; \ }; \ self.generator = ScalarCallbackFunction(newGenerator); \ } -#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ - scalar_operator operator op(double other, scalar_operator self) { \ - std::complex value(other, 0.0); \ - return self op value; \ - } - -#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(scalar_operator self, double other) { \ - std::complex value(other, 0.0); \ - return value op self; \ - } +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); #define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ void operator op(scalar_operator &self, double other) { \ - std::complex value(other, 0.0); \ - self op value; \ + /* Need to move the existing generating function to a new operator so that \ + * we can modify the generator in `self` in-place. */ \ + scalar_operator prevSelf(self); \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return prevSelf \ + .evaluate(parameters) op other; \ + }; \ + self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_DOUBLES(+); -ARITHMETIC_OPERATIONS_DOUBLES(-); -ARITHMETIC_OPERATIONS_DOUBLES(*); -ARITHMETIC_OPERATIONS_DOUBLES(/); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op(scalar_operator other) { \ - /* Create an operator that we will store the result in and return to the \ - * user. */ \ - scalar_operator returnOperator; \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - returnOperator._operators_to_compose.push_back(*this); \ - returnOperator._operators_to_compose.push_back(other); \ +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(const std::complex other, \ + const scalar_operator self) { \ auto newGenerator = \ [&](std::map> parameters) { \ - return returnOperator._operators_to_compose[0] \ - .evaluate(parameters) op returnOperator._operators_to_compose[1] \ - .evaluate(parameters); \ + return other op self.evaluate(parameters); \ }; \ - returnOperator.generator = ScalarCallbackFunction(newGenerator); \ - return returnOperator; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, scalar_operator other) { \ - /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in `self` in-place. */ \ - scalar_operator selfCopy(self); \ - /* Store the previous generator functions in the new operator. This is \ - * needed as the old generator functions would effectively be lost once we \ - * leave this function scope. */ \ - self._operators_to_compose.push_back(selfCopy); \ - self._operators_to_compose.push_back(other); \ +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); + +#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ + scalar_operator operator op(double other, const scalar_operator self) { \ auto newGenerator = \ [&](std::map> parameters) { \ - return self._operators_to_compose[0] \ - .evaluate(parameters) op self._operators_to_compose[1] \ - .evaluate(parameters); \ + return other op self.evaluate(parameters); \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_SCALAR_OPS(+); -ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -ARITHMETIC_OPERATIONS_SCALAR_OPS(*); -ARITHMETIC_OPERATIONS_SCALAR_OPS(/); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); template -operator_sum scalar_operator::operator+(elementary_operator other) { +operator_sum scalar_operator::operator+(const HandlerTy other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum({product_operator({*this}), product_operator({other})}); } template -operator_sum scalar_operator::operator-(elementary_operator other) { +operator_sum scalar_operator::operator-(const HandlerTy other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. return operator_sum( @@ -235,23 +193,23 @@ operator_sum scalar_operator::operator-(elementary_operator -product_operator scalar_operator::operator*(elementary_operator other) { +product_operator scalar_operator::operator*(const HandlerTy other) const { return product_operator({*this, other}); } template -operator_sum scalar_operator::operator+(product_operator other) { +operator_sum scalar_operator::operator+(const product_operator other) const { return operator_sum({product_operator({*this}), other}); } template -operator_sum scalar_operator::operator-(product_operator other) { +operator_sum scalar_operator::operator-(const product_operator other) const { return operator_sum({product_operator({*this}), (-1. * other)}); } template -product_operator scalar_operator::operator*(product_operator other) { - std::vector>> other_terms = +product_operator scalar_operator::operator*(const product_operator other) const { + std::vector> other_terms = other.get_terms(); /// Insert this scalar operator to the front of the terms list. other_terms.insert(other_terms.begin(), *this); @@ -259,14 +217,14 @@ product_operator scalar_operator::operator*(product_operator -operator_sum scalar_operator::operator+(operator_sum other) { +operator_sum scalar_operator::operator+(const operator_sum other) const { std::vector> other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); return operator_sum(other_terms); } template -operator_sum scalar_operator::operator-(operator_sum other) { +operator_sum scalar_operator::operator-(const operator_sum other) const { auto negative_other = (-1. * other); std::vector> other_terms = negative_other.get_terms(); other_terms.insert(other_terms.begin(), *this); @@ -274,7 +232,7 @@ operator_sum scalar_operator::operator-(operator_sum other } template -operator_sum scalar_operator::operator*(operator_sum other) { +operator_sum scalar_operator::operator*(const operator_sum other) const { std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) term = *this * term; diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index f5e4dca422..c2767b6af0 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -24,7 +24,6 @@ class operator_sum; template class product_operator; -template class elementary_operator; class scalar_operator; @@ -37,24 +36,23 @@ template // handler needs to inherit from operation_handler class operator_sum { private: - std::vector> m_terms; + std::vector> terms; - std::vector>> + std::vector> canonicalize_product(product_operator &prod) const; - std::vector>> + std::vector> _canonical_terms() const; public: - /// @brief Empty constructor that a user can aggregate terms into. - // operator_sum() = default; // FIXME: NEEDED? - /// @brief Construct a `cudaq::operator_sum` given a sequence of /// `cudaq::product_operator`'s. /// This operator expression represents a sum of terms, where each term /// is a product of elementary and scalar operators. operator_sum(const std::vector> &terms) - : m_terms(terms) {} + : terms(terms) {} + + ~operator_sum() = default; operator_sum canonicalize() const; @@ -82,8 +80,8 @@ class operator_sum { // Arithmetic operators operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(operator_sum &other) const; - operator_sum operator*=(operator_sum &other); + operator_sum operator*(const operator_sum &other) const; + operator_sum operator*=(const operator_sum &other); operator_sum operator+=(const operator_sum &other); operator_sum operator-=(const operator_sum &other); operator_sum operator*(const scalar_operator &other) const; @@ -92,12 +90,12 @@ class operator_sum { operator_sum operator*=(const scalar_operator &other); operator_sum operator+=(const scalar_operator &other); operator_sum operator-=(const scalar_operator &other); - operator_sum operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - operator_sum operator*=(std::complex other); - operator_sum operator+=(std::complex other); - operator_sum operator-=(std::complex other); + operator_sum operator*(const std::complex other) const; + operator_sum operator+(const std::complex other) const; + operator_sum operator-(const std::complex other) const; + operator_sum operator*=(const std::complex other); + operator_sum operator+=(const std::complex other); + operator_sum operator-=(const std::complex other); operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; @@ -110,18 +108,18 @@ class operator_sum { operator_sum operator*=(const product_operator &other); operator_sum operator+=(const product_operator &other); operator_sum operator-=(const product_operator &other); - operator_sum operator+(const elementary_operator &other) const; - operator_sum operator-(const elementary_operator &other) const; - operator_sum operator*(const elementary_operator &other) const; - operator_sum operator*=(const elementary_operator &other); - operator_sum operator+=(const elementary_operator &other); - operator_sum operator-=(const elementary_operator &other); + operator_sum operator+(const HandlerTy &other) const; + operator_sum operator-(const HandlerTy &other) const; + operator_sum operator*(const HandlerTy &other) const; + operator_sum operator*=(const HandlerTy &other); + operator_sum operator+=(const HandlerTy &other); + operator_sum operator-=(const HandlerTy &other); /// @brief Return the operator_sum as a string. std::string to_string() const; /// @brief Return the number of operator terms that make up this operator sum. - int term_count() const { return m_terms.size(); } + int term_count() const { return terms.size(); } /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator @@ -136,21 +134,21 @@ class operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() { return m_terms; } + std::vector> get_terms() const { return terms; } }; template -operator_sum operator*(std::complex other, operator_sum self); +operator_sum operator*(const std::complex other, const operator_sum self); template -operator_sum operator+(std::complex other, operator_sum self); +operator_sum operator+(const std::complex other, const operator_sum self); template -operator_sum operator-(std::complex other, operator_sum self); +operator_sum operator-(const std::complex other, const operator_sum self); template -operator_sum operator*(double other, operator_sum self); +operator_sum operator*(double other, const operator_sum self); template -operator_sum operator+(double other, operator_sum self); +operator_sum operator+(double other, const operator_sum self); template -operator_sum operator-(double other, operator_sum self); +operator_sum operator-(double other, const operator_sum self); /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within @@ -160,45 +158,44 @@ template // handler needs to inherit from operation_handler class product_operator : public operator_sum { private: - std::vector>> m_terms; + std::vector> ops; public: - // product_operator() = default; // FIXME: needed? - ~product_operator() = default; - // Constructor for an operator expression that represents a product // of scalar and elementary operators. // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. product_operator( - std::vector>> + std::vector> atomic_operators) - : m_terms(atomic_operators) {} + : operator_sum({*this}), ops(atomic_operators) {} + + ~product_operator() = default; // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - product_operator operator*=(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); + operator_sum operator+(const std::complex other) const; + operator_sum operator-(const std::complex other) const; + product_operator operator*(const std::complex other) const; + product_operator operator*=(const std::complex other); + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*(double other) const; product_operator operator*=(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); - product_operator operator*=(scalar_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - product_operator operator*=(product_operator other); - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - product_operator operator*=(elementary_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator*(operator_sum other); + operator_sum operator+(const scalar_operator other) const; + operator_sum operator-(const scalar_operator other) const; + product_operator operator*(const scalar_operator other) const; + product_operator operator*=(const scalar_operator other); + operator_sum operator+(const product_operator other) const; + operator_sum operator-(const product_operator other) const; + product_operator operator*(const product_operator other) const; + product_operator operator*=(const product_operator other); + operator_sum operator+(const HandlerTy other) const; + operator_sum operator-(const HandlerTy other) const; + product_operator operator*(const HandlerTy other) const; + product_operator operator*=(const HandlerTy other); + operator_sum operator+(const operator_sum other) const; + operator_sum operator-(const operator_sum other) const; + operator_sum operator*(const operator_sum other) const; /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator @@ -235,12 +232,12 @@ class product_operator : public operator_sum { /// @brief Return the number of operator terms that make up this product /// operator. - int term_count() const { return m_terms.size(); } + int term_count() const { return ops.size(); } /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector>> get_terms() { - return m_terms; + std::vector> get_terms() const { + return ops; }; }; @@ -257,8 +254,122 @@ operator_sum operator-(double other, product_operator self template product_operator operator*(double other, product_operator self); -template -class elementary_operator : public product_operator { +// FIXME: check if we really need the inheritance from prod operator, and if so what it should be +// (check if this really should be its own class?) +// -> replace elementary operator with the operator handler, the current elem op is the handler for custom op +// -> scalar remains its own op class, but likely doesn't need to be convertible to operator (i.e. no inheritance) +class scalar_operator { + +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::complex m_constant_value; + +public: + /// @brief Constructor that just takes a callback function with no + /// arguments. + + scalar_operator(ScalarCallbackFunction &&create) { + generator = ScalarCallbackFunction(create); + } + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value); + scalar_operator(double value); + + /// NOTE: We should revisit these constructors and remove any that have + /// become unnecessary as the implementation improves. + // scalar_operator() = default; + // Copy constructor. + // scalar_operator(const scalar_operator &other); + // scalar_operator(scalar_operator &other); + + ~scalar_operator() = default; + + // Arithmetic overloads against other operator types. + scalar_operator operator+(const scalar_operator other) const; + scalar_operator operator-(const scalar_operator other) const; + scalar_operator operator*(const scalar_operator other) const; + scalar_operator operator/(const scalar_operator other) const; + scalar_operator operator+(const std::complex other) const; + scalar_operator operator-(const std::complex other) const; + scalar_operator operator*(const std::complex other) const; + scalar_operator operator/(const std::complex other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + + void operator+=(const scalar_operator other); + void operator-=(const scalar_operator other); + void operator*=(const scalar_operator other); + void operator/=(const scalar_operator other); + void operator+=(const std::complex other); + void operator-=(const std::complex other); + void operator*=(const std::complex other); + void operator/=(const std::complex other); + void operator+=(double other); + void operator-=(double other); + void operator*=(double other); + void operator/=(double other); + + /// TODO: implement and test pow + scalar_operator pow(const scalar_operator other) const; + template + operator_sum operator+(const HandlerTy other) const; + template + operator_sum operator-(const HandlerTy other) const; + template + product_operator operator*(const HandlerTy other) const; + template + operator_sum operator+(const product_operator other) const; + template + operator_sum operator-(const product_operator other) const; + template + product_operator operator*(const product_operator other) const; + template + operator_sum operator+(const operator_sum other) const; + template + operator_sum operator-(const operator_sum other) const; + template + operator_sum operator*(const operator_sum other) const; + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(const std::map> parameters) const; + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatibility with the other inherited classes. + matrix_2 to_matrix(const std::map dimensions, + const std::map> parameters) const; + + // /// @brief Returns true if other is a scalar operator with the same + // /// generator. + // bool operator==(scalar_operator other); + + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; + + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {-1}; +}; + +scalar_operator operator+(const std::complex other, const scalar_operator self); +scalar_operator operator-(const std::complex other, const scalar_operator self); +scalar_operator operator*(const std::complex other, const scalar_operator self); +scalar_operator operator/(const std::complex other, const scalar_operator self); +scalar_operator operator+(double other, const scalar_operator self); +scalar_operator operator-(double other, const scalar_operator self); +scalar_operator operator*(double other, const scalar_operator self); +scalar_operator operator/(double other, const scalar_operator self); + +class elementary_operator : public product_operator { private: std::map m_ops; @@ -271,50 +382,53 @@ class elementary_operator : public product_operator { /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. elementary_operator(std::string operator_id, std::vector degrees) - : id(operator_id), degrees(degrees) {} + : id(operator_id), degrees(degrees), + product_operator({std::variant{*this}}) { } // Copy constructor. FIXME: NEEDED? - elementary_operator(const elementary_operator &other) + elementary_operator(const elementary_operator &other) : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} + degrees(other.degrees), id(other.id), + product_operator({std::variant{*this}}) { } - elementary_operator(elementary_operator &other) + elementary_operator(elementary_operator &other) : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id) {} + degrees(other.degrees), id(other.id), + product_operator({std::variant{*this}}) { } + + ~elementary_operator() = default; // Arithmetic overloads against all other operator types. - operator_sum operator+(std::complex other); - operator_sum operator-(std::complex other); - product_operator operator*(std::complex other); - operator_sum operator+(double other); - operator_sum operator-(double other); - product_operator operator*(double other); - operator_sum operator+(scalar_operator other); - operator_sum operator-(scalar_operator other); - product_operator operator*(scalar_operator other); + operator_sum operator+(const std::complex other) const; + operator_sum operator-(const std::complex other) const; + product_operator operator*(const std::complex other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*(double other) const; + operator_sum operator+(scalar_operator other) const; + operator_sum operator-(scalar_operator other) const; + product_operator operator*(scalar_operator other) const; // big question regarding templates is whether coefficients are part of product or are part of elem ops - // part of product seems more intuitive to me, and probably simplifies code; // that would mean even for eager prod/add, we still get a prod/sum as result - operator_sum operator+(elementary_operator other); - operator_sum operator-(elementary_operator other); - product_operator operator*(elementary_operator other); - operator_sum operator+(product_operator other); - operator_sum operator-(product_operator other); - product_operator operator*(product_operator other); - operator_sum operator+(operator_sum other); - operator_sum operator-(operator_sum other); - operator_sum operator+=(operator_sum other); - operator_sum operator-=(operator_sum other); - operator_sum operator*(operator_sum other); + operator_sum operator+(const elementary_operator other) const; + operator_sum operator-(const elementary_operator other) const; + product_operator operator*(const elementary_operator other) const; + operator_sum operator+(const product_operator other) const; + operator_sum operator-(const product_operator other) const; + product_operator operator*(const product_operator other) const; + operator_sum operator+(const operator_sum other) const; + operator_sum operator-(const operator_sum other) const; + operator_sum operator*(const operator_sum other) const; /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + bool operator==(elementary_operator other); - /// @brief Return the `elementary_operator` as a string. + /// @brief Return the `elementary_operator` as a string. std::string to_string() const; - /// @brief Return the `elementary_operator` as a matrix. + /// @brief Return the `elementary_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -324,18 +438,18 @@ class elementary_operator : public product_operator { const std::map> parameters) const; // Predefined operators. - static elementary_operator identity(int degree); - static elementary_operator zero(int degree); - static elementary_operator annihilate(int degree); - static elementary_operator create(int degree); - static elementary_operator momentum(int degree); - static elementary_operator number(int degree); - static elementary_operator parity(int degree); - static elementary_operator position(int degree); + static elementary_operator identity(int degree); + static elementary_operator zero(int degree); + static elementary_operator annihilate(int degree); + static elementary_operator create(int degree); + static elementary_operator momentum(int degree); + static elementary_operator number(int degree); + static elementary_operator parity(int degree); + static elementary_operator position(int degree); /// FIXME: - static elementary_operator squeeze(int degree, + static elementary_operator squeeze(int degree, std::complex amplitude); - static elementary_operator displace(int degree, + static elementary_operator displace(int degree, std::complex amplitude); /// @brief Adds the definition of an elementary operator with the given id to @@ -387,139 +501,22 @@ class elementary_operator : public product_operator { /// order. std::vector degrees; std::string id; - - // /// @brief Creates a representation of the operator as `pauli_word` that - // can be passed as an argument to quantum kernels. - // pauli_word to_pauli_word ovveride(); }; + // Reverse order arithmetic for elementary operators against pure scalars. template -operator_sum operator+(std::complex other, elementary_operator self); +operator_sum operator+(const std::complex other, const elementary_operator self); template -operator_sum operator-(std::complex other, elementary_operator self); +operator_sum operator-(const std::complex other, const elementary_operator self); template -product_operator operator*(std::complex other, - elementary_operator self); +product_operator operator*(const std::complex other, + const elementary_operator self); template -operator_sum operator+(double other, elementary_operator self); +operator_sum operator+(double other, const elementary_operator self); template -operator_sum operator-(double other, elementary_operator self); +operator_sum operator-(double other, const elementary_operator self); template -product_operator operator*(double other, elementary_operator self); - -// FIXME: check if we really need the inheritance from prod operator, and if so what it should be -// (check if this really should be its own class?) -class scalar_operator { - -private: - // If someone gave us a constant value, we will just return that - // directly to them when they call `evaluate`. - std::complex m_constant_value; - -public: - /// @brief Constructor that just takes a callback function with no - /// arguments. - - scalar_operator(ScalarCallbackFunction &&create) { - generator = ScalarCallbackFunction(create); - } - - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value); - scalar_operator(double value); - - // Arithmetic overloads against other operator types. - scalar_operator operator+(scalar_operator other); - scalar_operator operator-(scalar_operator other); - scalar_operator operator*(scalar_operator other); - scalar_operator operator/(scalar_operator other); - /// TODO: implement and test pow - scalar_operator pow(scalar_operator other); - template - operator_sum operator+(elementary_operator other); - template - operator_sum operator-(elementary_operator other); - template - product_operator operator*(elementary_operator other); - template - operator_sum operator+(product_operator other); - template - operator_sum operator-(product_operator other); - template - product_operator operator*(product_operator other); - template - operator_sum operator+(operator_sum other); - template - operator_sum operator-(operator_sum other); - template - operator_sum operator*(operator_sum other); - - /// @brief Return the scalar operator as a concrete complex value. - std::complex - evaluate(std::map> parameters) const; - - // Return the scalar operator as a 1x1 matrix. This is needed for - // compatibility with the other inherited classes. - matrix_2 to_matrix(std::map dimensions, - std::map> parameters); - - // /// @brief Returns true if other is a scalar operator with the same - // /// generator. - // bool operator==(scalar_operator other); - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - - // Only populated when we've performed arithmetic between various - // scalar operators. - std::vector _operators_to_compose; - - /// NOTE: We should revisit these constructors and remove any that have - /// become unnecessary as the implementation improves. - scalar_operator() = default; - // Copy constructor. - scalar_operator(const scalar_operator &other); - scalar_operator(scalar_operator &other); - - ~scalar_operator() = default; - - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {-1}; - - // REMOVEME: just using this as a temporary patch: - std::complex get_val() { return m_constant_value; }; -}; - -scalar_operator operator+(scalar_operator self, std::complex other); -scalar_operator operator-(scalar_operator self, std::complex other); -scalar_operator operator*(scalar_operator self, std::complex other); -scalar_operator operator/(scalar_operator self, std::complex other); -scalar_operator operator+(std::complex other, scalar_operator self); -scalar_operator operator-(std::complex other, scalar_operator self); -scalar_operator operator*(std::complex other, scalar_operator self); -scalar_operator operator/(std::complex other, scalar_operator self); -scalar_operator operator+(scalar_operator self, double other); -scalar_operator operator-(scalar_operator self, double other); -scalar_operator operator*(scalar_operator self, double other); -scalar_operator operator/(scalar_operator self, double other); -scalar_operator operator+(double other, scalar_operator self); -scalar_operator operator-(double other, scalar_operator self); -scalar_operator operator*(double other, scalar_operator self); -scalar_operator operator/(double other, scalar_operator self); -void operator+=(scalar_operator &self, std::complex other); -void operator-=(scalar_operator &self, std::complex other); -void operator*=(scalar_operator &self, std::complex other); -void operator/=(scalar_operator &self, std::complex other); -void operator+=(scalar_operator &self, scalar_operator other); -void operator-=(scalar_operator &self, scalar_operator other); -void operator*=(scalar_operator &self, scalar_operator other); -void operator/=(scalar_operator &self, scalar_operator other); +product_operator operator*(double other, const elementary_operator self); /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian : public operator_sum { From 46fe3871176f6989bf96328f454e18eee758f214 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 19:37:02 +0000 Subject: [PATCH 166/311] wip to use elementary_operator as matrix handler Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 175 ++++---- runtime/cudaq/dynamics/operator_sum.cpp | 316 ++++++++------ runtime/cudaq/dynamics/product_operators.cpp | 214 ++++++---- runtime/cudaq/dynamics/scalar_operators.cpp | 206 ++++------ runtime/cudaq/operators.h | 388 +++++++++--------- 5 files changed, 650 insertions(+), 649 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index baa5df8396..a13d0d1aa2 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -269,85 +269,116 @@ elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } +// evaluations + matrix_2 elementary_operator::to_matrix( std::map dimensions, std::map> parameters) { return m_ops[id].generator(dimensions, parameters); } -/// Elementary Operator Arithmetic. +// right-hand arithmetics + +product_operator elementary_operator::operator*(double other) const { + std::complex value(other, 0.0); + return *this * value; +} + +operator_sum elementary_operator::operator+(double other) const { + std::complex value(other, 0.0); + return *this + value; +} + +operator_sum elementary_operator::operator-(double other) const { + std::complex value(other, 0.0); + return *this - value; +} + +product_operator elementary_operator::operator*(std::complex other) const { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + *this, other_scalar}; + return product_operator(_args); +} -operator_sum elementary_operator::operator+(const scalar_operator other) const { +operator_sum elementary_operator::operator+(std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator(other); std::vector> _this = { *this}; std::vector> _other = { - other}; + other_scalar}; return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(const scalar_operator other) const { +operator_sum elementary_operator::operator-(std::complex other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. + auto other_scalar = scalar_operator((-1. * other)); std::vector> _this = { *this}; std::vector> _other = { - -1. * other}; + other_scalar}; return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(const scalar_operator other) const { +product_operator elementary_operator::operator*(const scalar_operator &other) const { std::vector> _args = { *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(const std::complex other) const { +operator_sum elementary_operator::operator+(const scalar_operator &other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator(other); std::vector> _this = { *this}; std::vector> _other = { - other_scalar}; + other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(const std::complex other) const { +operator_sum elementary_operator::operator-(const scalar_operator &other) const { // Operator sum is composed of product operators, so we must convert // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator((-1. * other)); std::vector> _this = { *this}; std::vector> _other = { - other_scalar}; + -1. * other}; return operator_sum({product_operator(_this), product_operator(_other)}); } -product_operator elementary_operator::operator*(const std::complex other) const { - auto other_scalar = scalar_operator(other); +product_operator elementary_operator::operator*(const elementary_operator &other) const { std::vector> _args = { - *this, other_scalar}; + *this, other}; return product_operator(_args); } -operator_sum elementary_operator::operator+(double other) const { - std::complex value(other, 0.0); - return *this + value; +operator_sum elementary_operator::operator+(const elementary_operator &other) const { + std::vector> _this = { + *this}; + std::vector> _other = { + other}; + return operator_sum({product_operator(_this), product_operator(_other)}); } -operator_sum elementary_operator::operator-(double other) const { - std::complex value(other, 0.0); - return *this - value; +operator_sum elementary_operator::operator-(const elementary_operator &other) const { + std::vector> _this = { + *this}; + return operator_sum({product_operator(_this), (-1. * other)}); } -product_operator elementary_operator::operator*(double other) const { - std::complex value(other, 0.0); - return *this * value; +// left-hand arithmetics + +product_operator operator*(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); } -operator_sum operator+(const std::complex other, const elementary_operator self) { +operator_sum operator+(double other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _self = { self}; @@ -356,109 +387,51 @@ operator_sum operator+(const std::complex other, co return operator_sum({product_operator(_other), product_operator(_self)}); } -template -operator_sum operator-(const std::complex other, const elementary_operator self) { +operator_sum operator-(double other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); + return operator_sum({product_operator(_other), (-1. * self)}); } -template -product_operator operator*(const std::complex other, - const elementary_operator self) { +product_operator operator*(std::complex other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _args = { other_scalar, self}; return product_operator(_args); } -template -operator_sum operator+(double other, const elementary_operator self) { +operator_sum operator+(std::complex other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _self = { self}; std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum({product_operator(_other), product_operator(_self)}); } -template -operator_sum operator-(double other, const elementary_operator self) { +operator_sum operator-(std::complex other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); -} - -template -product_operator operator*(double other, const elementary_operator self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); -} - -product_operator elementary_operator::operator*(const elementary_operator other) const { - std::vector> _args = { - *this, other}; - return product_operator(_args); -} - -operator_sum elementary_operator::operator+(const elementary_operator other) const { - std::vector> _this = { - *this}; - std::vector> _other = { - other}; - return operator_sum({product_operator(_this), product_operator(_other)}); -} - -operator_sum elementary_operator::operator-(const elementary_operator other) const { - std::vector> _this = { - *this}; - return operator_sum({product_operator(_this), (-1. * other)}); + return operator_sum({product_operator(_other), (-1. * self)}); } -operator_sum elementary_operator::operator+(const operator_sum other) const { - std::vector> _this = { - *this}; - std::vector> _prods = {product_operator(_this)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + other; -} - -operator_sum elementary_operator::operator-(const operator_sum other) const { - std::vector> _this = { - *this}; - std::vector> _prods = {product_operator(_this)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - other; +product_operator operator*(const scalar_operator &other, const elementary_operator &self) { + return product_operator({other, self}); } -operator_sum elementary_operator::operator*(const operator_sum other) const { - std::vector> _this = { - *this}; - std::vector> _prods = {product_operator(_this)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * other; -} - -operator_sum elementary_operator::operator+(const product_operator other) const { - std::vector> _this = { - *this}; - return operator_sum({product_operator(_this), other}); -} - -operator_sum elementary_operator::operator-(const product_operator other) const { - return *this + (-1. * other); +operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum({product_operator({other}), product_operator({self})}); } -product_operator elementary_operator::operator*(const product_operator other) const { - std::vector> other_terms = - other.get_terms(); - /// Insert this elementary operator to the front of the terms list. - other_terms.insert(other_terms.begin(), *this); - return product_operator(other_terms); +operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { + // Operator sum is composed of product operators, so we must convert + // both underlying types to `product_operators` to perform the arithmetic. + return operator_sum( + {product_operator({other}), product_operator({-1. * self})}); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index e3a3e97e45..e2c238e932 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -118,53 +118,91 @@ namespace cudaq { template class operator_sum; -// Arithmetic operators +// evaluations + +/// FIXME: +// tensor +// operator_sum::to_matrix(const std::map &dimensions, +// const std::map ¶ms) const { +// // todo +// } + +// std::string operator_sum::to_string() const { +// std::string result; +// // for (const auto &term : terms) { +// // result += term.to_string() + " + "; +// // } +// // // Remove last " + " +// // if (!result.empty()) +// // result.pop_back(); +// return result; +// } + +// right-hand arithmetics + template -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = terms; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.terms.begin()), - std::make_move_iterator(other.terms.end())); - return operator_sum(combined_terms); +operator_sum operator_sum::operator*(double other) const { + return *this * scalar_operator(other); } template -operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (-1 * other); +operator_sum operator_sum::operator+(double other) const { + return *this + scalar_operator(other); } -//template -//operator_sum operator_sum::operator-( -// const operator_sum &other) const; +template +operator_sum operator_sum::operator-(double other) const { + return *this - scalar_operator(other); +} template -operator_sum operator_sum::operator-=(const operator_sum &other) { - *this = *this - other; +operator_sum operator_sum::operator*=(double other) { + *this *= scalar_operator(other); return *this; } template -operator_sum operator_sum::operator+=(const operator_sum &other) { - *this = *this + other; +operator_sum operator_sum::operator+=(double other) { + *this += scalar_operator(other); return *this; } template -operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = terms; - std::vector> product_terms; - auto other_terms = other.get_terms(); - for (auto &term : self_terms) { - for (auto &other_term : other_terms) { - product_terms.push_back(term * other_term); - } - } - return operator_sum(product_terms); +operator_sum operator_sum::operator-=(double other) { + *this -= scalar_operator(other); + return *this; } template -operator_sum operator_sum::operator*=(const operator_sum &other) { - *this = *this * other; +operator_sum operator_sum::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +operator_sum operator_sum::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +template +operator_sum operator_sum::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +template +operator_sum operator_sum::operator*=(std::complex other) { + *this *= scalar_operator(other); + return *this; +} + +template +operator_sum operator_sum::operator+=(std::complex other) { + *this += scalar_operator(other); + return *this; +} + +template +operator_sum operator_sum::operator-=(std::complex other) { + *this -= scalar_operator(other); return *this; } @@ -210,203 +248,217 @@ operator_sum operator_sum::operator-=(const scalar_operato } template -operator_sum operator_sum::operator*(std::complex other) const { - return *this * scalar_operator(other); +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator+(std::complex other) const { - return *this + scalar_operator(other); +operator_sum operator_sum::operator+(const HandlerTy &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator-(std::complex other) const { - return *this - scalar_operator(other); +operator_sum operator_sum::operator-(const HandlerTy &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back((-1. * other)); + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator*=(std::complex other) { - *this *= scalar_operator(other); +operator_sum operator_sum::operator*=(const HandlerTy &other) { + *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(std::complex other) { - *this += scalar_operator(other); +operator_sum operator_sum::operator+=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this + product_operator(_other); return *this; } template -operator_sum operator_sum::operator-=(std::complex other) { - *this -= scalar_operator(other); +operator_sum operator_sum::operator-=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this - product_operator(_other); return *this; } template -operator_sum operator_sum::operator*(double other) const { - return *this * scalar_operator(other); +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator+(double other) const { - return *this + scalar_operator(other); +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other); + return operator_sum(combined_terms); } template -operator_sum operator_sum::operator-(double other) const { - return *this - scalar_operator(other); +operator_sum operator_sum::operator-(const product_operator &other) const { + return *this + (-1. * other); } template -operator_sum operator_sum::operator*=(double other) { - *this *= scalar_operator(other); +operator_sum operator_sum::operator*=(const product_operator &other) { + *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(double other) { - *this += scalar_operator(other); +operator_sum operator_sum::operator+=(const product_operator &other) { + *this = *this + other; return *this; } template -operator_sum operator_sum::operator-=(double other) { - *this -= scalar_operator(other); +operator_sum operator_sum::operator-=(const product_operator &other) { + *this = *this - other; return *this; } template -operator_sum operator*(std::complex other, operator_sum self) { - return scalar_operator(other) * self; +operator_sum operator_sum::operator*(const operator_sum &other) const { + auto self_terms = terms; + std::vector> product_terms; + auto other_terms = other.get_terms(); + for (auto &term : self_terms) { + for (auto &other_term : other_terms) { + product_terms.push_back(term * other_term); + } + } + return operator_sum(product_terms); } template -operator_sum operator+(std::complex other, operator_sum self) { - return scalar_operator(other) + self; +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector> combined_terms = terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.terms.begin()), + std::make_move_iterator(other.terms.end())); + return operator_sum(combined_terms); } template -operator_sum operator-(std::complex other, operator_sum self) { - return scalar_operator(other) - self; +operator_sum operator_sum::operator-(const operator_sum &other) const { + return *this + (-1 * other); } +//template +//operator_sum operator_sum::operator-( +// const operator_sum &other) const; + template -operator_sum operator*(double other, operator_sum self) { - return scalar_operator(other) * self; +operator_sum operator_sum::operator*=(const operator_sum &other) { + *this = *this * other; + return *this; } template -operator_sum operator+(double other, operator_sum self) { - return scalar_operator(other) + self; +operator_sum operator_sum::operator-=(const operator_sum &other) { + *this = *this - other; + return *this; } template -operator_sum operator-(double other, operator_sum self) { - return scalar_operator(other) - self; +operator_sum operator_sum::operator+=(const operator_sum &other) { + *this = *this + other; + return *this; } +// left-hand arithmetics + template -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back(other); - return operator_sum(combined_terms); +operator_sum operator*(double other, const operator_sum &self) { + return scalar_operator(other) * self; } template -operator_sum operator_sum::operator+=(const product_operator &other) { - *this = *this + other; - return *this; +operator_sum operator+(double other, const operator_sum &self) { + return scalar_operator(other) + self; } template -operator_sum operator_sum::operator-(const product_operator &other) const { - return *this + (-1. * other); +operator_sum operator-(double other, const operator_sum &self) { + return scalar_operator(other) - self; } template -operator_sum operator_sum::operator-=(const product_operator &other) { - *this = *this - other; - return *this; +operator_sum operator*(std::complex other, const operator_sum &self) { + return scalar_operator(other) * self; } template -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); +operator_sum operator+(std::complex other, const operator_sum &self) { + return scalar_operator(other) + self; } template -operator_sum operator_sum::operator*=(const product_operator &other) { - *this = *this * other; - return *this; +operator_sum operator-(std::complex other, const operator_sum &self) { + return scalar_operator(other) - self; } template -operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); - return operator_sum(combined_terms); +operator_sum operator*(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + for (auto &term : terms) + term = other * term; + return operator_sum(terms); } template -operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back((-1. * other)); - return operator_sum(combined_terms); +operator_sum operator+(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); } template -operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); +operator_sum operator-(const scalar_operator &other, const operator_sum &self) { + auto negative_self = (-1. * self); + std::vector> terms = negative_self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); } template -operator_sum operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this + product_operator(_other); - return *this; +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * self; } template -operator_sum operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this - product_operator(_other); - return *this; +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + self; } template -operator_sum operator_sum::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - self; } -/// FIXME: -// tensor -// operator_sum::to_matrix(const std::map &dimensions, -// const std::map ¶ms) const { -// // todo -// } - -// std::string operator_sum::to_string() const { -// std::string result; -// // for (const auto &term : terms) { -// // result += term.to_string() + " + "; -// // } -// // // Remove last " + " -// // if (!result.empty()) -// // result.pop_back(); -// return result; -// } - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 293fb2cfc1..8db05cd0da 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -163,182 +163,226 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } + +// right-hand arithmetics + template -operator_sum product_operator::operator+(const scalar_operator other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); +product_operator product_operator::operator*(double other) const { + return *this * scalar_operator(other); } template -operator_sum product_operator::operator-(const scalar_operator other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); +operator_sum product_operator::operator+(double other) const { + return *this + scalar_operator(other); } template -product_operator product_operator::operator*(const scalar_operator other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); +operator_sum product_operator::operator-(double other) const { + return *this - scalar_operator(other); } template -product_operator product_operator::operator*=(const scalar_operator other) { - *this = *this * other; +product_operator product_operator::operator*=(double other) { + *this = *this * scalar_operator(other); return *this; } template -operator_sum product_operator::operator+(const std::complex other) const { +product_operator product_operator::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +operator_sum product_operator::operator+(std::complex other) const { return *this + scalar_operator(other); } template -operator_sum product_operator::operator-(const std::complex other) const { +operator_sum product_operator::operator-(std::complex other) const { return *this - scalar_operator(other); } template -product_operator product_operator::operator*(const std::complex other) const { - return *this * scalar_operator(other); +product_operator product_operator::operator*=(std::complex other) { + *this = *this * scalar_operator(other); + return *this; } template -product_operator product_operator::operator*=(const std::complex other) { - *this = *this * scalar_operator(other); +product_operator product_operator::operator*(const scalar_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +template +operator_sum product_operator::operator+(const scalar_operator &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +template +operator_sum product_operator::operator-(const scalar_operator &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); +} + +template +product_operator product_operator::operator*=(const scalar_operator &other) { + *this = *this * other; return *this; } template -operator_sum operator+(const std::complex other, const product_operator self) { - return operator_sum({scalar_operator(other), self}); +product_operator product_operator::operator*(const HandlerTy &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); } template -operator_sum operator-(const std::complex other, const product_operator self) { - return scalar_operator(other) - self; +operator_sum product_operator::operator+(const HandlerTy &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); } template -product_operator operator*(const std::complex other, const product_operator self) { - return scalar_operator(other) * self; +operator_sum product_operator::operator-(const HandlerTy &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, -1. * product_operator(_other)}); } template -operator_sum product_operator::operator+(double other) const { - return *this + scalar_operator(other); +product_operator product_operator::operator*=(const HandlerTy &other) { + *this = *this * other; + return *this; } template -operator_sum product_operator::operator-(double other) const { - return *this - scalar_operator(other); +product_operator product_operator::operator*(const product_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.ops.begin()), + std::make_move_iterator(other.ops.end())); + return product_operator(combined_terms); } template -product_operator product_operator::operator*(double other) const { - return *this * scalar_operator(other); +operator_sum product_operator::operator+(const product_operator &other) const { + return operator_sum({*this, other}); } template -product_operator product_operator::operator*=(double other) { - *this = *this * scalar_operator(other); +operator_sum product_operator::operator-(const product_operator &other) const { + return operator_sum({*this, (-1. * other)}); +} + +template +product_operator product_operator::operator*=(const product_operator &other) { + *this = *this * other; return *this; } template -operator_sum operator+(double other, const product_operator self) { - return operator_sum({scalar_operator(other), self}); +operator_sum product_operator::operator*(const operator_sum &other) const { + std::vector> other_terms = other.get_terms(); + for (auto &term : other_terms) { + term = *this * term; + } + return operator_sum(other_terms); } template -operator_sum operator-(double other, const product_operator self) { - return scalar_operator(other) - self; +operator_sum product_operator::operator+(const operator_sum &other) const { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); } template -product_operator operator*(double other, const product_operator self) { +operator_sum product_operator::operator-(const operator_sum &other) const { + auto negative_other = (-1. * other); + std::vector> other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +// left-hand arithmetics + +template +product_operator operator*(double other, const product_operator &self) { return scalar_operator(other) * self; } template -operator_sum product_operator::operator+(const product_operator other) const { - return operator_sum({*this, other}); +operator_sum operator+(double other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum product_operator::operator-(const product_operator other) const { - return operator_sum({*this, (-1. * other)}); +operator_sum operator-(double other, const product_operator &self) { + return scalar_operator(other) - self; } template -product_operator product_operator::operator*(const product_operator other) const { - std::vector> - combined_terms = ops; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.ops.begin()), - std::make_move_iterator(other.ops.end())); - return product_operator(combined_terms); +product_operator operator*(const std::complex other, const product_operator &self) { + return scalar_operator(other) * self; } template -product_operator product_operator::operator*=(const product_operator other) { - *this = *this * other; - return *this; +operator_sum operator+(const std::complex other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); } template -operator_sum product_operator::operator+(const HandlerTy other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); +operator_sum operator-(const std::complex other, const product_operator &self) { + return scalar_operator(other) - self; } template -operator_sum product_operator::operator-(const HandlerTy other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); +product_operator operator*(const scalar_operator &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this scalar operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); } template -product_operator product_operator::operator*(const HandlerTy other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); +operator_sum operator+(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); } template -product_operator product_operator::operator*=(const HandlerTy other) { - *this = *this * other; - return *this; +operator_sum operator-(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), (-1. * self)}); } template -operator_sum product_operator::operator+(const operator_sum other) const { - std::vector other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this elementary operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); } template -operator_sum product_operator::operator-(const operator_sum other) const { - auto negative_other = (-1. * other); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + std::vector> new_term = {other}; + return operator_sum({product_operator(new_term), self}); } template -operator_sum product_operator::operator*(const operator_sum other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) { - term = *this * term; - } - return operator_sum(other_terms); +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return other + (-1. * self); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index a31a1257e5..dbd1c046b7 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,6 +14,8 @@ namespace cudaq { +// constructors + /// @brief Constructor that just takes and returns a complex double value. scalar_operator::scalar_operator(std::complex value) { m_constant_value = value; @@ -33,6 +35,8 @@ scalar_operator::scalar_operator(double value) { generator = ScalarCallbackFunction(func); } +// evaluations + std::complex scalar_operator::evaluate( const std::map> parameters) const { return generator(parameters); @@ -46,37 +50,7 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const scalar_operator other) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return self \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_SCALAR_OPS(+); -ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -ARITHMETIC_OPERATIONS_SCALAR_OPS(*); -ARITHMETIC_OPERATIONS_SCALAR_OPS(/); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const std::complex other) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return self.evaluate(parameters) op other; \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); +// right-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ scalar_operator operator op(const scalar_operator &self, double other) { \ @@ -88,32 +62,46 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_DOUBLES(+); -ARITHMETIC_OPERATIONS_DOUBLES(-); ARITHMETIC_OPERATIONS_DOUBLES(*); ARITHMETIC_OPERATIONS_DOUBLES(/); +ARITHMETIC_OPERATIONS_DOUBLES(+); +ARITHMETIC_OPERATIONS_DOUBLES(-); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, const scalar_operator other) { \ - /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in `self` in-place. */ \ +#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, double other) { \ + /* Need to move the existing generating function to a new operator so that \ + * we can modify the generator in `self` in-place. */ \ scalar_operator prevSelf(self); \ auto newGenerator = \ [&](std::map> parameters) { \ return prevSelf \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ + .evaluate(parameters) op other; \ }; \ self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ + scalar_operator operator op(const scalar_operator &self, \ + const std::complex other) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return self.evaluate(parameters) op other; \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, const std::complex other) { \ + void operator op(scalar_operator &self, std::complex other) { \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in `self` in-place. */ \ scalar_operator prevSelf(self); \ @@ -125,46 +113,51 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); -#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, double other) { \ - /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator operator op(const scalar_operator &self, \ + const scalar_operator &other) { \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other; \ + return self \ + .evaluate(parameters) op other \ + .evaluate(parameters); \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_SCALAR_OPS(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(const std::complex other, \ - const scalar_operator self) { \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + void operator op(scalar_operator &self, const scalar_operator &other) { \ + /* Need to move the existing generating function to a new operator so \ + * that we can modify the generator in `self` in-place. */ \ + scalar_operator prevSelf(self); \ auto newGenerator = \ [&](std::map> parameters) { \ - return other op self.evaluate(parameters); \ + return prevSelf \ + .evaluate(parameters) op other \ + .evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + self.generator = ScalarCallbackFunction(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); + +// left-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(double other, const scalar_operator self) { \ + scalar_operator operator op(double other, const scalar_operator &self) { \ auto newGenerator = \ [&](std::map> parameters) { \ return other op self.evaluate(parameters); \ @@ -172,71 +165,24 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); -template -operator_sum scalar_operator::operator+(const HandlerTy other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum({product_operator({*this}), product_operator({other})}); -} - -template -operator_sum scalar_operator::operator-(const HandlerTy other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum( - {product_operator({*this}), product_operator({-1. * other})}); -} - -template -product_operator scalar_operator::operator*(const HandlerTy other) const { - return product_operator({*this, other}); -} - -template -operator_sum scalar_operator::operator+(const product_operator other) const { - return operator_sum({product_operator({*this}), other}); -} - -template -operator_sum scalar_operator::operator-(const product_operator other) const { - return operator_sum({product_operator({*this}), (-1. * other)}); -} - -template -product_operator scalar_operator::operator*(const product_operator other) const { - std::vector> other_terms = - other.get_terms(); - /// Insert this scalar operator to the front of the terms list. - other_terms.insert(other_terms.begin(), *this); - return product_operator(other_terms); -} - -template -operator_sum scalar_operator::operator+(const operator_sum other) const { - std::vector> other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - -template -operator_sum scalar_operator::operator-(const operator_sum other) const { - auto negative_other = (-1. * other); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(std::complex other, \ + const scalar_operator &self) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return other op self.evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } -template -operator_sum scalar_operator::operator*(const operator_sum other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) - term = *this * term; - return operator_sum(other_terms); -} +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index c2767b6af0..6769e4aabb 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -78,42 +78,42 @@ class operator_sum { const std::map> ¶ms = {}) const; // Arithmetic operators - operator_sum operator+(const operator_sum &other) const; - operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(const operator_sum &other) const; - operator_sum operator*=(const operator_sum &other); - operator_sum operator+=(const operator_sum &other); - operator_sum operator-=(const operator_sum &other); - operator_sum operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*=(const scalar_operator &other); - operator_sum operator+=(const scalar_operator &other); - operator_sum operator-=(const scalar_operator &other); - operator_sum operator*(const std::complex other) const; - operator_sum operator+(const std::complex other) const; - operator_sum operator-(const std::complex other) const; - operator_sum operator*=(const std::complex other); - operator_sum operator+=(const std::complex other); - operator_sum operator-=(const std::complex other); operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; operator_sum operator*=(double other); operator_sum operator+=(double other); operator_sum operator-=(double other); - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; - operator_sum operator*=(const product_operator &other); - operator_sum operator+=(const product_operator &other); - operator_sum operator-=(const product_operator &other); + operator_sum operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + operator_sum operator*=(std::complex other); + operator_sum operator+=(std::complex other); + operator_sum operator-=(std::complex other); + operator_sum operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + operator_sum operator*=(const scalar_operator &other); + operator_sum operator+=(const scalar_operator &other); + operator_sum operator-=(const scalar_operator &other); operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; operator_sum operator*=(const HandlerTy &other); operator_sum operator+=(const HandlerTy &other); operator_sum operator-=(const HandlerTy &other); + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + operator_sum operator*=(const product_operator &other); + operator_sum operator+=(const product_operator &other); + operator_sum operator-=(const product_operator &other); + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; + operator_sum operator*(const operator_sum &other) const; + operator_sum operator*=(const operator_sum &other); + operator_sum operator+=(const operator_sum &other); + operator_sum operator-=(const operator_sum &other); /// @brief Return the operator_sum as a string. std::string to_string() const; @@ -137,18 +137,31 @@ class operator_sum { std::vector> get_terms() const { return terms; } }; +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); template -operator_sum operator*(const std::complex other, const operator_sum self); +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); template -operator_sum operator+(const std::complex other, const operator_sum self); +operator_sum operator-(std::complex other, const operator_sum &self); template -operator_sum operator-(const std::complex other, const operator_sum self); +operator_sum operator*(const scalar_operator &other, const operator_sum &self); template -operator_sum operator*(double other, const operator_sum self); +operator_sum operator+(const scalar_operator &other, const operator_sum &self); template -operator_sum operator+(double other, const operator_sum self); +operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -operator_sum operator-(double other, const operator_sum self); +operator_sum operator*(const HandlerTy &other, const operator_sum self); +template +operator_sum operator+(const HandlerTy &other, const operator_sum self); +template +operator_sum operator-(const HandlerTy &other, const operator_sum self); + /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within @@ -172,41 +185,19 @@ class product_operator : public operator_sum { ~product_operator() = default; - // Arithmetic overloads against all other operator types. - operator_sum operator+(const std::complex other) const; - operator_sum operator-(const std::complex other) const; - product_operator operator*(const std::complex other) const; - product_operator operator*=(const std::complex other); - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(double other) const; - product_operator operator*=(double other); - operator_sum operator+(const scalar_operator other) const; - operator_sum operator-(const scalar_operator other) const; - product_operator operator*(const scalar_operator other) const; - product_operator operator*=(const scalar_operator other); - operator_sum operator+(const product_operator other) const; - operator_sum operator-(const product_operator other) const; - product_operator operator*(const product_operator other) const; - product_operator operator*=(const product_operator other); - operator_sum operator+(const HandlerTy other) const; - operator_sum operator-(const HandlerTy other) const; - product_operator operator*(const HandlerTy other) const; - product_operator operator*=(const HandlerTy other); - operator_sum operator+(const operator_sum other) const; - operator_sum operator-(const operator_sum other) const; - operator_sum operator*(const operator_sum other) const; + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. - bool operator==(product_operator other); + /// @brief Return the number of operator terms that make up this product + /// operator. + int term_count() const { return ops.size(); } + + /// FIXME: Protect this once I can do deeper testing in `unittests`. + // protected: + std::vector> get_terms() const { + return ops; + }; /// @brief Return the `product_operator` as a string. std::string to_string() const; @@ -222,37 +213,68 @@ class product_operator : public operator_sum { to_matrix(const std::map dimensions, const std::map> parameters) const; - /// @brief Creates a representation of the operator as a `cudaq::pauli_word` - /// that can be passed as an argument to quantum kernels. - // pauli_word to_pauli_word(); - - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees() const; - - /// @brief Return the number of operator terms that make up this product - /// operator. - int term_count() const { return ops.size(); } + // Arithmetic overloads against all other operator types. + product_operator operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*=(double other); + product_operator operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + product_operator operator*=(std::complex other); + product_operator operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + product_operator operator*=(const scalar_operator &other); + product_operator operator*(const HandlerTy &other) const; + operator_sum operator+(const HandlerTy &other) const; + operator_sum operator-(const HandlerTy &other) const; + product_operator operator*=(const HandlerTy &other); + product_operator operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; + product_operator operator*=(const product_operator &other); + operator_sum operator*(const operator_sum &other) const; + operator_sum operator+(const operator_sum &other) const; + operator_sum operator-(const operator_sum &other) const; - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: - std::vector> get_terms() const { - return ops; - }; + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms `blockwise`; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(product_operator other); }; template -operator_sum operator+(std::complex other, product_operator self); +product_operator operator*(double other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); template -operator_sum operator-(std::complex other, product_operator self); +operator_sum operator+(std::complex other, const product_operator &self); template -product_operator operator*(std::complex other, product_operator self); +operator_sum operator-(std::complex other, const product_operator &self); template -operator_sum operator+(double other, product_operator self); +product_operator operator*(const scalar_operator &other, const product_operator &self); template -operator_sum operator-(double other, product_operator self); +operator_sum operator+(const scalar_operator &other, const product_operator &self); template -product_operator operator*(double other, product_operator self); +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +operator_sum operator-(const HandlerTy &other, const product_operator &self); + // FIXME: check if we really need the inheritance from prod operator, and if so what it should be // (check if this really should be its own class?) @@ -288,53 +310,15 @@ class scalar_operator { ~scalar_operator() = default; - // Arithmetic overloads against other operator types. - scalar_operator operator+(const scalar_operator other) const; - scalar_operator operator-(const scalar_operator other) const; - scalar_operator operator*(const scalar_operator other) const; - scalar_operator operator/(const scalar_operator other) const; - scalar_operator operator+(const std::complex other) const; - scalar_operator operator-(const std::complex other) const; - scalar_operator operator*(const std::complex other) const; - scalar_operator operator/(const std::complex other) const; - scalar_operator operator+(double other) const; - scalar_operator operator-(double other) const; - scalar_operator operator*(double other) const; - scalar_operator operator/(double other) const; - - void operator+=(const scalar_operator other); - void operator-=(const scalar_operator other); - void operator*=(const scalar_operator other); - void operator/=(const scalar_operator other); - void operator+=(const std::complex other); - void operator-=(const std::complex other); - void operator*=(const std::complex other); - void operator/=(const std::complex other); - void operator+=(double other); - void operator-=(double other); - void operator*=(double other); - void operator/=(double other); + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; - /// TODO: implement and test pow - scalar_operator pow(const scalar_operator other) const; - template - operator_sum operator+(const HandlerTy other) const; - template - operator_sum operator-(const HandlerTy other) const; - template - product_operator operator*(const HandlerTy other) const; - template - operator_sum operator+(const product_operator other) const; - template - operator_sum operator-(const product_operator other) const; - template - product_operator operator*(const product_operator other) const; - template - operator_sum operator+(const operator_sum other) const; - template - operator_sum operator-(const operator_sum other) const; - template - operator_sum operator*(const operator_sum other) const; + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {}; /// @brief Return the scalar operator as a concrete complex value. std::complex @@ -345,29 +329,46 @@ class scalar_operator { matrix_2 to_matrix(const std::map dimensions, const std::map> parameters) const; + // Arithmetic overloads against other operator types. + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + void operator*=(double other); + void operator/=(double other); + void operator+=(double other); + void operator-=(double other); + scalar_operator operator*(std::complex other) const; + scalar_operator operator/(std::complex other) const; + scalar_operator operator+(std::complex other) const; + scalar_operator operator-(std::complex other) const; + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; + void operator*=(std::complex other); + void operator/=(std::complex other); + void operator+=(std::complex other); + void operator-=(std::complex other); + void operator*=(const scalar_operator &other); + void operator/=(const scalar_operator &other); + void operator+=(const scalar_operator &other); + void operator-=(const scalar_operator &other); + /// TODO: implement and test pow + // /// @brief Returns true if other is a scalar operator with the same // /// generator. // bool operator==(scalar_operator other); - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {-1}; }; -scalar_operator operator+(const std::complex other, const scalar_operator self); -scalar_operator operator-(const std::complex other, const scalar_operator self); -scalar_operator operator*(const std::complex other, const scalar_operator self); -scalar_operator operator/(const std::complex other, const scalar_operator self); -scalar_operator operator+(double other, const scalar_operator self); -scalar_operator operator-(double other, const scalar_operator self); -scalar_operator operator*(double other, const scalar_operator self); -scalar_operator operator/(double other, const scalar_operator self); +scalar_operator operator*(double other, const scalar_operator &self); +scalar_operator operator/(double other, const scalar_operator &self); +scalar_operator operator+(double other, const scalar_operator &self); +scalar_operator operator-(double other, const scalar_operator &self); +scalar_operator operator*(std::complex other, const scalar_operator &self); +scalar_operator operator/(std::complex other, const scalar_operator &self); +scalar_operator operator+(std::complex other, const scalar_operator &self); +scalar_operator operator-(std::complex other, const scalar_operator &self); class elementary_operator : public product_operator { @@ -398,32 +399,15 @@ class elementary_operator : public product_operator { ~elementary_operator() = default; - // Arithmetic overloads against all other operator types. - operator_sum operator+(const std::complex other) const; - operator_sum operator-(const std::complex other) const; - product_operator operator*(const std::complex other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(double other) const; - operator_sum operator+(scalar_operator other) const; - operator_sum operator-(scalar_operator other) const; - product_operator operator*(scalar_operator other) const; - // big question regarding templates is whether coefficients are part of product or are part of elem ops - - // part of product seems more intuitive to me, and probably simplifies code; - // that would mean even for eager prod/add, we still get a prod/sum as result - operator_sum operator+(const elementary_operator other) const; - operator_sum operator-(const elementary_operator other) const; - product_operator operator*(const elementary_operator other) const; - operator_sum operator+(const product_operator other) const; - operator_sum operator-(const product_operator other) const; - product_operator operator*(const product_operator other) const; - operator_sum operator+(const operator_sum other) const; - operator_sum operator-(const operator_sum other) const; - operator_sum operator*(const operator_sum other) const; - - /// @brief True, if the other value is an elementary operator with the same id - /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + /// @brief The number of levels, that is the dimension, for each degree of + /// freedom in canonical order that the operator acts on. A value of zero or + /// less indicates that the operator is defined for any dimension of that + /// degree. + std::map expected_dimensions; + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees; + std::string id; /// @brief Return the `elementary_operator` as a string. std::string to_string() const; @@ -437,6 +421,24 @@ class elementary_operator : public product_operator { to_matrix(const std::map dimensions, const std::map> parameters) const; + // Arithmetic overloads + product_operator operator*(double other) const; + operator_sum operator+(double other) const; + operator_sum operator-(double other) const; + product_operator operator*(std::complex other) const; + operator_sum operator+(std::complex other) const; + operator_sum operator-(std::complex other) const; + product_operator operator*(const scalar_operator &other) const; + operator_sum operator+(const scalar_operator &other) const; + operator_sum operator-(const scalar_operator &other) const; + product_operator operator*(const elementary_operator &other) const; + operator_sum operator+(const elementary_operator &other) const; + operator_sum operator-(const elementary_operator &other) const; + + /// @brief True, if the other value is an elementary operator with the same id + /// acting on the same degrees of freedom, and False otherwise. + bool operator==(elementary_operator other); + // Predefined operators. static elementary_operator identity(int degree); static elementary_operator zero(int degree); @@ -489,34 +491,18 @@ class elementary_operator : public product_operator { defn.create_definition(operator_id, expected_dimensions, create); m_ops[operator_id] = defn; } - - // Attributes. - - /// @brief The number of levels, that is the dimension, for each degree of - /// freedom in canonical order that the operator acts on. A value of zero or - /// less indicates that the operator is defined for any dimension of that - /// degree. - std::map expected_dimensions; - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees; - std::string id; }; // Reverse order arithmetic for elementary operators against pure scalars. -template -operator_sum operator+(const std::complex other, const elementary_operator self); -template -operator_sum operator-(const std::complex other, const elementary_operator self); -template -product_operator operator*(const std::complex other, - const elementary_operator self); -template -operator_sum operator+(double other, const elementary_operator self); -template -operator_sum operator-(double other, const elementary_operator self); -template -product_operator operator*(double other, const elementary_operator self); +product_operator operator*(double other, const elementary_operator &self); +operator_sum operator+(double other, const elementary_operator &self); +operator_sum operator-(double other, const elementary_operator &self); +product_operator operator*(std::complex other, const elementary_operator &self); +operator_sum operator+(std::complex other, const elementary_operator &self); +operator_sum operator-(std::complex other, const elementary_operator &self); +product_operator operator*(const scalar_operator &other, const elementary_operator &self); +operator_sum operator+(const scalar_operator &other, const elementary_operator &self); +operator_sum operator-(const scalar_operator &other, const elementary_operator &self); /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian : public operator_sum { From 9912357c8b3dc346501f5d51fa405dd2c53da363 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 20:29:09 +0000 Subject: [PATCH 167/311] kinded of more cleaned up but left-hand arithmethics need more thought Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 18 +++++++----------- runtime/cudaq/dynamics/operator_sum.cpp | 10 ++++------ runtime/cudaq/dynamics/product_operators.cpp | 12 ++++-------- runtime/cudaq/operators.h | 14 +++++++------- 4 files changed, 22 insertions(+), 32 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index a13d0d1aa2..021e81e534 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -411,27 +411,23 @@ operator_sum operator+(std::complex other, const el } operator_sum operator-(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _other = { - other_scalar}; + std::vector> _other = {scalar_operator(other)}; return operator_sum({product_operator(_other), (-1. * self)}); } product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator({other, self}); + return product_operator({other, self}); } operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum({product_operator({other}), product_operator({self})}); + std::vector> _other = {other}; + std::vector> _self = {self}; + return operator_sum({product_operator(_other), product_operator(_self)}); } operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - return operator_sum( - {product_operator({other}), product_operator({-1. * self})}); + std::vector> _other = {other}; + return operator_sum({product_operator(_other), -1. * self}); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index e2c238e932..f83f09e680 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -312,7 +312,9 @@ operator_sum operator_sum::operator+(const product_operato template operator_sum operator_sum::operator-(const product_operator &other) const { - return *this + (-1. * other); + std::vector> combined_terms = terms; + combined_terms.push_back(other * (-1.)); + return operator_sum(combined_terms); } template @@ -357,13 +359,9 @@ operator_sum operator_sum::operator+(const operator_sum operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (-1 * other); + return *this + (other * (-1)); } -//template -//operator_sum operator_sum::operator-( -// const operator_sum &other) const; - template operator_sum operator_sum::operator*=(const operator_sum &other) { *this = *this * other; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 8db05cd0da..b91f05f9aa 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -163,7 +163,6 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } - // right-hand arithmetics template @@ -225,9 +224,7 @@ operator_sum product_operator::operator+(const scalar_oper template operator_sum product_operator::operator-(const scalar_operator &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, product_operator({-1. * other})}); } template @@ -253,9 +250,7 @@ operator_sum product_operator::operator+(const HandlerTy & template operator_sum product_operator::operator-(const HandlerTy &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, -1. * product_operator(_other)}); + return operator_sum({*this, -1. * other}); } template @@ -281,7 +276,8 @@ operator_sum product_operator::operator+(const product_ope template operator_sum product_operator::operator-(const product_operator &other) const { - return operator_sum({*this, (-1. * other)}); + std::vector> combined_terms = {*this, -1. * other}; + return operator_sum(combined_terms); } template diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6769e4aabb..98319c9721 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -156,11 +156,11 @@ operator_sum operator+(const scalar_operator &other, const operator_s template operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -operator_sum operator*(const HandlerTy &other, const operator_sum self); +operator_sum operator*(const HandlerTy &other, const operator_sum &self); template -operator_sum operator+(const HandlerTy &other, const operator_sum self); +operator_sum operator+(const HandlerTy &other, const operator_sum &self); template -operator_sum operator-(const HandlerTy &other, const operator_sum self); +operator_sum operator-(const HandlerTy &other, const operator_sum &self); /// @brief Represents an operator expression consisting of a product of @@ -342,14 +342,14 @@ class scalar_operator { scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - scalar_operator operator*(const scalar_operator &other) const; - scalar_operator operator/(const scalar_operator &other) const; - scalar_operator operator+(const scalar_operator &other) const; - scalar_operator operator-(const scalar_operator &other) const; void operator*=(std::complex other); void operator/=(std::complex other); void operator+=(std::complex other); void operator-=(std::complex other); + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; void operator*=(const scalar_operator &other); void operator/=(const scalar_operator &other); void operator+=(const scalar_operator &other); From 02fc1258290a833009b90656cace4eb224cdebc6 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 21:49:53 +0000 Subject: [PATCH 168/311] constrain the templates Signed-off-by: Bettina Heim --- runtime/cudaq/operators.h | 39 ++++++++++++++++++++++++++++++++++----- unittests/CMakeLists.txt | 11 ++++------- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 98319c9721..be22981d26 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -15,24 +15,28 @@ #include #include #include +#include namespace cudaq { -template -class operator_sum; +class scalar_operator; + +class elementary_operator; template +requires std::derived_from class product_operator; -class elementary_operator; - -class scalar_operator; +template +requires std::derived_from +class operator_sum; /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. template // handler needs to inherit from operation_handler +requires std::derived_from class operator_sum { private: @@ -138,28 +142,40 @@ class operator_sum { }; template +requires std::derived_from operator_sum operator*(double other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(double other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(double other, const operator_sum &self); template +requires std::derived_from operator_sum operator*(std::complex other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(std::complex other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(std::complex other, const operator_sum &self); template +requires std::derived_from operator_sum operator*(const scalar_operator &other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self); template +requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self); template +requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self); template +requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self); @@ -168,6 +184,7 @@ operator_sum operator-(const HandlerTy &other, const operat /// quantum kernels, but they provide methods to convert them to data types /// that can. template // handler needs to inherit from operation_handler +requires std::derived_from class product_operator : public operator_sum { private: @@ -251,28 +268,40 @@ class product_operator : public operator_sum { }; template +requires std::derived_from product_operator operator*(double other, const product_operator &self); template +requires std::derived_from operator_sum operator+(double other, const product_operator &self); template +requires std::derived_from operator_sum operator-(double other, const product_operator &self); template +requires std::derived_from product_operator operator*(std::complex other, const product_operator &self); template +requires std::derived_from operator_sum operator+(std::complex other, const product_operator &self); template +requires std::derived_from operator_sum operator-(std::complex other, const product_operator &self); template +requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self); template +requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self); template +requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self); template +requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self); template +requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self); template +requires std::derived_from operator_sum operator-(const HandlerTy &other, const product_operator &self); diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index ecc2d68116..77437e5d31 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -44,11 +44,6 @@ set(CUDAQ_RUNTIME_TEST_SOURCES common/NoiseModelTester.cpp integration/tracer_tester.cpp integration/gate_library_tester.cpp - dynamics/scalar_ops_simple.cpp - dynamics/scalar_ops_arithmetic.cpp - dynamics/elementary_ops_simple.cpp - dynamics/elementary_ops_arithmetic.cpp - dynamics/product_operators_arithmetic.cpp dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp dynamics/rydberg_hamiltonian.cpp @@ -80,8 +75,10 @@ macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) endif() target_link_libraries(${TEST_EXE_NAME} PUBLIC - nvqir-${NVQIR_BACKEND} nvqir - cudaq fmt::fmt-header-only + nvqir-${NVQIR_BACKEND} + nvqir + cudaq + fmt::fmt-header-only cudaq-platform-default cudaq-builder gtest_main From 071b115091b0ccf40159a6cc490cdab0ddc0a3a6 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 14 Jan 2025 22:50:51 +0000 Subject: [PATCH 169/311] making the left hand arithmetics friends Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 122 ++++++------- runtime/cudaq/dynamics/operator_sum.cpp | 158 ++++++++-------- runtime/cudaq/dynamics/product_operators.cpp | 142 +++++++-------- runtime/cudaq/dynamics/scalar_operators.cpp | 62 +++---- runtime/cudaq/operators.h | 172 ++++++++---------- 5 files changed, 317 insertions(+), 339 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 021e81e534..3f3805539c 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -277,6 +277,67 @@ matrix_2 elementary_operator::to_matrix( return m_ops[id].generator(dimensions, parameters); } +// left-hand arithmetics + +product_operator operator*(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +operator_sum operator+(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(double other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(std::complex other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _args = { + other_scalar, self}; + return product_operator(_args); +} + +operator_sum operator+(std::complex other, const elementary_operator &self) { + auto other_scalar = scalar_operator(other); + std::vector> _self = { + self}; + std::vector> _other = { + other_scalar}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(std::complex other, const elementary_operator &self) { + std::vector> _other = {scalar_operator(other)}; + return operator_sum({product_operator(_other), (-1. * self)}); +} + +product_operator operator*(const scalar_operator &other, const elementary_operator &self) { + return product_operator({other, self}); +} + +operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { + std::vector> _other = {other}; + std::vector> _self = {self}; + return operator_sum({product_operator(_other), product_operator(_self)}); +} + +operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { + std::vector> _other = {other}; + return operator_sum({product_operator(_other), -1. * self}); +} + // right-hand arithmetics product_operator elementary_operator::operator*(double other) const { @@ -369,65 +430,4 @@ operator_sum elementary_operator::operator-(const elementar return operator_sum({product_operator(_this), (-1. * other)}); } -// left-hand arithmetics - -product_operator operator*(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); -} - -operator_sum operator+(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); -} - -operator_sum operator-(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _other = { - other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); -} - -product_operator operator*(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); -} - -operator_sum operator+(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); -} - -operator_sum operator-(std::complex other, const elementary_operator &self) { - std::vector> _other = {scalar_operator(other)}; - return operator_sum({product_operator(_other), (-1. * self)}); -} - -product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator({other, self}); -} - -operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - std::vector> _self = {self}; - return operator_sum({product_operator(_other), product_operator(_self)}); -} - -operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - return operator_sum({product_operator(_other), -1. * self}); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index f83f09e680..5a2ea2780c 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -138,6 +138,85 @@ template class operator_sum; // return result; // } +// left-hand arithmetics + +template +operator_sum operator*(double other, const operator_sum &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(double other, const operator_sum &self) { + return scalar_operator(other) + self; +} + +template +operator_sum operator-(double other, const operator_sum &self) { + return scalar_operator(other) - self; +} + +template +operator_sum operator*(std::complex other, const operator_sum &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(std::complex other, const operator_sum &self) { + return scalar_operator(other) + self; +} + +template +operator_sum operator-(std::complex other, const operator_sum &self) { + return scalar_operator(other) - self; +} + +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + for (auto &term : terms) + term = other * term; + return operator_sum(terms); +} + +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); +} + +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self) { + auto negative_self = (-1. * self); + std::vector> terms = negative_self.get_terms(); + terms.insert(terms.begin(), other); + return operator_sum(terms); +} + +template +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * self; +} + +template +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + self; +} + +template +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - self; +} + // right-hand arithmetics template @@ -380,83 +459,4 @@ operator_sum operator_sum::operator+=(const operator_sum -operator_sum operator*(double other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(double other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(std::complex other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(std::complex other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(std::complex other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - for (auto &term : terms) - term = other * term; - return operator_sum(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self) { - auto negative_self = (-1. * self); - std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * self; -} - -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + self; -} - -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - self; -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index b91f05f9aa..1aba9c915a 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -163,6 +163,77 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } +// left-hand arithmetics + +template +product_operator operator*(double other, const product_operator &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(double other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); +} + +template +operator_sum operator-(double other, const product_operator &self) { + return scalar_operator(other) - self; +} + +template +product_operator operator*(const std::complex other, const product_operator &self) { + return scalar_operator(other) * self; +} + +template +operator_sum operator+(const std::complex other, const product_operator &self) { + return operator_sum({scalar_operator(other), self}); +} + +template +operator_sum operator-(const std::complex other, const product_operator &self) { + return scalar_operator(other) - self; +} + +template +product_operator operator*(const scalar_operator &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this scalar operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +operator_sum operator+(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); +} + +template +operator_sum operator-(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), (-1. * self)}); +} + +template +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector> terms = + self.get_terms(); + /// Insert this elementary operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + std::vector> new_term = {other}; + return operator_sum({product_operator(new_term), self}); +} + +template +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return other + (-1. * self); +} + // right-hand arithmetics template @@ -310,75 +381,4 @@ operator_sum product_operator::operator-(const operator_su return operator_sum(other_terms); } -// left-hand arithmetics - -template -product_operator operator*(double other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(double other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const std::complex other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(const std::complex other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const scalar_operator &other, const product_operator &self) { - std::vector> terms = - self.get_terms(); - /// Insert this scalar operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); -} - -template -operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), (-1. * self)}); -} - -template -product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector> terms = - self.get_terms(); - /// Insert this elementary operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - std::vector> new_term = {other}; - return operator_sum({product_operator(new_term), self}); -} - -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return other + (-1. * self); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index dbd1c046b7..0bab3de20f 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -50,6 +50,37 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +// left-hand arithmetics + +#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ + scalar_operator operator op(double other, const scalar_operator &self) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return other op self.evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); + +#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ + scalar_operator operator op(std::complex other, \ + const scalar_operator &self) { \ + auto newGenerator = \ + [&](std::map> parameters) { \ + return other op self.evaluate(parameters); \ + }; \ + return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + } + +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); +ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); + // right-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ @@ -154,35 +185,4 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); -// left-hand arithmetics - -#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(double other, const scalar_operator &self) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(std::complex other, \ - const scalar_operator &self) { \ - auto newGenerator = \ - [&](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index be22981d26..6f61e9f342 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -70,6 +70,9 @@ class operator_sum { // template // TEval _evaluate(OperatorArithmetics &arithmetics) const; + /// @brief Return the operator_sum as a string. + std::string to_string() const; + /// @brief Return the `operator_sum` as a matrix. /// @arg `dimensions` : A mapping that specifies the number of levels, /// that is, the dimension of each degree of freedom @@ -119,8 +122,32 @@ class operator_sum { operator_sum operator+=(const operator_sum &other); operator_sum operator-=(const operator_sum &other); - /// @brief Return the operator_sum as a string. - std::string to_string() const; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-template-friend" +#endif +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif + friend operator_sum operator*(double other, const operator_sum &self); + friend operator_sum operator+(double other, const operator_sum &self); + friend operator_sum operator-(double other, const operator_sum &self); + friend operator_sum operator*(std::complex other, const operator_sum &self); + friend operator_sum operator+(std::complex other, const operator_sum &self); + friend operator_sum operator-(std::complex other, const operator_sum &self); + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif /// @brief Return the number of operator terms that make up this operator sum. int term_count() const { return terms.size(); } @@ -141,44 +168,6 @@ class operator_sum { std::vector> get_terms() const { return terms; } }; -template -requires std::derived_from -operator_sum operator*(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const operator_sum &self); - - /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types @@ -255,6 +244,33 @@ class product_operator : public operator_sum { operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wnon-template-friend" +#endif +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wnon-template-friend" +#endif + friend product_operator operator*(double other, const product_operator &self); + friend operator_sum operator+(double other, const product_operator &self); + friend operator_sum operator-(double other, const product_operator &self); + friend product_operator operator*(std::complex other, const product_operator &self); + friend operator_sum operator+(std::complex other, const product_operator &self); + friend operator_sum operator-(std::complex other, const product_operator &self); + friend product_operator operator*(const scalar_operator &other, const product_operator &self); + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + friend product_operator operator*(const HandlerTy &other, const product_operator &self); + friend operator_sum operator+(const HandlerTy &other, const product_operator &self); + friend operator_sum operator-(const HandlerTy &other, const product_operator &self); +#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) +#pragma GCC diagnostic pop +#endif +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + /// @brief True, if the other value is an operator_sum with equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they @@ -267,44 +283,6 @@ class product_operator : public operator_sum { bool operator==(product_operator other); }; -template -requires std::derived_from -product_operator operator*(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(double other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const product_operator &self); - - // FIXME: check if we really need the inheritance from prod operator, and if so what it should be // (check if this really should be its own class?) // -> replace elementary operator with the operator handler, the current elem op is the handler for custom op @@ -385,19 +363,20 @@ class scalar_operator { void operator-=(const scalar_operator &other); /// TODO: implement and test pow + friend scalar_operator operator*(double other, const scalar_operator &self); + friend scalar_operator operator/(double other, const scalar_operator &self); + friend scalar_operator operator+(double other, const scalar_operator &self); + friend scalar_operator operator-(double other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, const scalar_operator &self); + friend scalar_operator operator/(std::complex other, const scalar_operator &self); + friend scalar_operator operator+(std::complex other, const scalar_operator &self); + friend scalar_operator operator-(std::complex other, const scalar_operator &self); + // /// @brief Returns true if other is a scalar operator with the same // /// generator. // bool operator==(scalar_operator other); }; -scalar_operator operator*(double other, const scalar_operator &self); -scalar_operator operator/(double other, const scalar_operator &self); -scalar_operator operator+(double other, const scalar_operator &self); -scalar_operator operator-(double other, const scalar_operator &self); -scalar_operator operator*(std::complex other, const scalar_operator &self); -scalar_operator operator/(std::complex other, const scalar_operator &self); -scalar_operator operator+(std::complex other, const scalar_operator &self); -scalar_operator operator-(std::complex other, const scalar_operator &self); class elementary_operator : public product_operator { @@ -464,6 +443,16 @@ class elementary_operator : public product_operator { operator_sum operator+(const elementary_operator &other) const; operator_sum operator-(const elementary_operator &other) const; + friend product_operator operator*(double other, const elementary_operator &self); + friend operator_sum operator+(double other, const elementary_operator &self); + friend operator_sum operator-(double other, const elementary_operator &self); + friend product_operator operator*(std::complex other, const elementary_operator &self); + friend operator_sum operator+(std::complex other, const elementary_operator &self); + friend operator_sum operator-(std::complex other, const elementary_operator &self); + friend product_operator operator*(const scalar_operator &other, const elementary_operator &self); + friend operator_sum operator+(const scalar_operator &other, const elementary_operator &self); + friend operator_sum operator-(const scalar_operator &other, const elementary_operator &self); + /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. bool operator==(elementary_operator other); @@ -522,17 +511,6 @@ class elementary_operator : public product_operator { } }; -// Reverse order arithmetic for elementary operators against pure scalars. -product_operator operator*(double other, const elementary_operator &self); -operator_sum operator+(double other, const elementary_operator &self); -operator_sum operator-(double other, const elementary_operator &self); -product_operator operator*(std::complex other, const elementary_operator &self); -operator_sum operator+(std::complex other, const elementary_operator &self); -operator_sum operator-(std::complex other, const elementary_operator &self); -product_operator operator*(const scalar_operator &other, const elementary_operator &self); -operator_sum operator+(const scalar_operator &other, const elementary_operator &self); -operator_sum operator-(const scalar_operator &other, const elementary_operator &self); - /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian : public operator_sum { public: From c12d4c9c37cc6010d08225cb43eba96d68a01d6b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 15 Jan 2025 13:19:28 +0000 Subject: [PATCH 170/311] cleaned up assignments Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 2 + runtime/cudaq/dynamics/operator_sum.cpp | 48 ++++++------ runtime/cudaq/dynamics/product_operators.cpp | 16 ++-- runtime/cudaq/dynamics/scalar_operators.cpp | 58 +++++++------- runtime/cudaq/operators.h | 79 +++++++++++--------- 5 files changed, 103 insertions(+), 100 deletions(-) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 63129f246f..3818446488 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -32,6 +32,8 @@ endif() add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) +target_compile_definitions(${LIBRARY_NAME} PRIVATE -DCUDAQ_INSTANTIATE_TEMPLATES) + target_include_directories(${LIBRARY_NAME} PUBLIC $ diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 5a2ea2780c..0fcf5fe70a 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -23,7 +23,7 @@ namespace cudaq { // std::vector scalars; // std::vector non_scalars; -// for (const auto &op : prod.get_terms()) { +// for (const auto &op : prod.get_operators()) { // if (std::holds_alternative(op)) { // scalars.push_back(*std::get(op)); // } else { @@ -83,7 +83,7 @@ namespace cudaq { // std::vector operator_sum::degrees() const { // std::set unique_degrees; // for (const auto &term : terms) { -// for (const auto &op : term.get_terms()) { +// for (const auto &op : term.get_operators()) { // unique_degrees.insert(op.get_degrees().begin(), // op.get_degrees().end()); // } @@ -96,7 +96,7 @@ namespace cudaq { // std::map operator_sum::parameters() const { // std::map param_map; // for (const auto &term : terms) { -// for (const auto &op : term.get_terms()) { +// for (const auto &op : term.get_operators()) { // auto op_params = op.parameters(); // param_map.insert(op_params.begin(), op.params.end()); // } @@ -109,15 +109,13 @@ namespace cudaq { // bool operator_sum::_is_spinop() const { // return std::all_of( // terms.begin(), terms.end(), [](product_operator &term) { -// return std::all_of(term.get_terms().begin(), -// term.get_terms().end(), +// return std::all_of(term.get_operators().begin(), +// term.get_operators().end(), // [](const Operator &op) { return op.is_spinop(); // }); // }); // } -template class operator_sum; - // evaluations /// FIXME: @@ -235,19 +233,19 @@ operator_sum operator_sum::operator-(double other) const { } template -operator_sum operator_sum::operator*=(double other) { +operator_sum& operator_sum::operator*=(double other) { *this *= scalar_operator(other); return *this; } template -operator_sum operator_sum::operator+=(double other) { +operator_sum& operator_sum::operator+=(double other) { *this += scalar_operator(other); return *this; } template -operator_sum operator_sum::operator-=(double other) { +operator_sum& operator_sum::operator-=(double other) { *this -= scalar_operator(other); return *this; } @@ -268,19 +266,19 @@ operator_sum operator_sum::operator-(std::complex } template -operator_sum operator_sum::operator*=(std::complex other) { +operator_sum& operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); return *this; } template -operator_sum operator_sum::operator+=(std::complex other) { +operator_sum& operator_sum::operator+=(std::complex other) { *this += scalar_operator(other); return *this; } template -operator_sum operator_sum::operator-=(std::complex other) { +operator_sum& operator_sum::operator-=(std::complex other) { *this -= scalar_operator(other); return *this; } @@ -309,19 +307,19 @@ operator_sum operator_sum::operator-(const scalar_operator } template -operator_sum operator_sum::operator*=(const scalar_operator &other) { +operator_sum& operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(const scalar_operator &other) { +operator_sum& operator_sum::operator+=(const scalar_operator &other) { *this = *this + other; return *this; } template -operator_sum operator_sum::operator-=(const scalar_operator &other) { +operator_sum& operator_sum::operator-=(const scalar_operator &other) { *this = *this - other; return *this; } @@ -352,13 +350,13 @@ operator_sum operator_sum::operator-(const HandlerTy &othe } template -operator_sum operator_sum::operator*=(const HandlerTy &other) { +operator_sum& operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(const HandlerTy &other) { +operator_sum& operator_sum::operator+=(const HandlerTy &other) { std::vector> _other = { other}; *this = *this + product_operator(_other); @@ -366,7 +364,7 @@ operator_sum operator_sum::operator+=(const HandlerTy &oth } template -operator_sum operator_sum::operator-=(const HandlerTy &other) { +operator_sum& operator_sum::operator-=(const HandlerTy &other) { std::vector> _other = { other}; *this = *this - product_operator(_other); @@ -397,19 +395,19 @@ operator_sum operator_sum::operator-(const product_operato } template -operator_sum operator_sum::operator*=(const product_operator &other) { +operator_sum& operator_sum::operator*=(const product_operator &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator+=(const product_operator &other) { +operator_sum& operator_sum::operator+=(const product_operator &other) { *this = *this + other; return *this; } template -operator_sum operator_sum::operator-=(const product_operator &other) { +operator_sum& operator_sum::operator-=(const product_operator &other) { *this = *this - other; return *this; } @@ -442,19 +440,19 @@ operator_sum operator_sum::operator-(const operator_sum -operator_sum operator_sum::operator*=(const operator_sum &other) { +operator_sum& operator_sum::operator*=(const operator_sum &other) { *this = *this * other; return *this; } template -operator_sum operator_sum::operator-=(const operator_sum &other) { +operator_sum& operator_sum::operator-=(const operator_sum &other) { *this = *this - other; return *this; } template -operator_sum operator_sum::operator+=(const operator_sum &other) { +operator_sum& operator_sum::operator+=(const operator_sum &other) { *this = *this + other; return *this; } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 1aba9c915a..af83948370 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -142,8 +142,6 @@ matrix_2 product_operator::to_matrix( // return out; // } -template class product_operator; - // Degrees property template std::vector product_operator::degrees() const { @@ -198,7 +196,7 @@ operator_sum operator-(const std::complex other, const produc template product_operator operator*(const scalar_operator &other, const product_operator &self) { std::vector> terms = - self.get_terms(); + self.get_operators(); /// Insert this scalar operator to the front of the terms list. terms.insert(terms.begin(), other); return product_operator(terms); @@ -217,7 +215,7 @@ operator_sum operator-(const scalar_operator &other, const product_op template product_operator operator*(const HandlerTy &other, const product_operator &self) { std::vector> terms = - self.get_terms(); + self.get_operators(); /// Insert this elementary operator to the front of the terms list. terms.insert(terms.begin(), other); return product_operator(terms); @@ -252,7 +250,7 @@ operator_sum product_operator::operator-(double other) con } template -product_operator product_operator::operator*=(double other) { +product_operator& product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } @@ -273,7 +271,7 @@ operator_sum product_operator::operator-(std::complex -product_operator product_operator::operator*=(std::complex other) { +product_operator& product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } @@ -299,7 +297,7 @@ operator_sum product_operator::operator-(const scalar_oper } template -product_operator product_operator::operator*=(const scalar_operator &other) { +product_operator& product_operator::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } @@ -325,7 +323,7 @@ operator_sum product_operator::operator-(const HandlerTy & } template -product_operator product_operator::operator*=(const HandlerTy &other) { +product_operator& product_operator::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } @@ -352,7 +350,7 @@ operator_sum product_operator::operator-(const product_ope } template -product_operator product_operator::operator*=(const product_operator &other) { +product_operator& product_operator::operator*=(const product_operator &other) { *this = *this * other; return *this; } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 0bab3de20f..24ce886a65 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -84,11 +84,10 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); // right-hand arithmetics #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ - scalar_operator operator op(const scalar_operator &self, double other) { \ + scalar_operator scalar_operator::operator op(double other) const { \ auto newGenerator = \ [&](std::map> parameters) { \ - return self \ - .evaluate(parameters) op other; \ + return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } @@ -99,16 +98,16 @@ ARITHMETIC_OPERATIONS_DOUBLES(+); ARITHMETIC_OPERATIONS_DOUBLES(-); #define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, double other) { \ + scalar_operator& scalar_operator::operator op(double other) { \ /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ + * we can modify the generator in-place. */ \ + scalar_operator prevSelf(*this); \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other; \ + return prevSelf.evaluate(parameters) op other; \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + this->generator = ScalarCallbackFunction(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); @@ -117,11 +116,11 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const std::complex other) { \ + scalar_operator scalar_operator::operator op( \ + const std::complex other) const{ \ auto newGenerator = \ [&](std::map> parameters) { \ - return self.evaluate(parameters) op other; \ + return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } @@ -132,16 +131,17 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, std::complex other) { \ + scalar_operator& scalar_operator::operator op( \ + std::complex other) { \ /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ + * we can modify the generator in-place. */ \ + scalar_operator prevSelf(*this); \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other; \ + return prevSelf.evaluate(parameters) op other; \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + this->generator = ScalarCallbackFunction(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); @@ -150,13 +150,11 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator operator op(const scalar_operator &self, \ - const scalar_operator &other) { \ + scalar_operator scalar_operator::operator op( \ + const scalar_operator &other) const { \ auto newGenerator = \ [&](std::map> parameters) { \ - return self \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ + return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ } @@ -167,17 +165,17 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - void operator op(scalar_operator &self, const scalar_operator &other) { \ + scalar_operator& scalar_operator::operator op( \ + const scalar_operator &other) { \ /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in `self` in-place. */ \ - scalar_operator prevSelf(self); \ + * that we can modify the generator in-place. */ \ + scalar_operator prevSelf(*this); \ auto newGenerator = \ [&](std::map> parameters) { \ - return prevSelf \ - .evaluate(parameters) op other \ - .evaluate(parameters); \ + return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ }; \ - self.generator = ScalarCallbackFunction(newGenerator); \ + this->generator = ScalarCallbackFunction(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6f61e9f342..d356c03598 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -88,39 +88,39 @@ class operator_sum { operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - operator_sum operator*=(double other); - operator_sum operator+=(double other); - operator_sum operator-=(double other); + operator_sum& operator*=(double other); + operator_sum& operator+=(double other); + operator_sum& operator-=(double other); operator_sum operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - operator_sum operator*=(std::complex other); - operator_sum operator+=(std::complex other); - operator_sum operator-=(std::complex other); + operator_sum& operator*=(std::complex other); + operator_sum& operator+=(std::complex other); + operator_sum& operator-=(std::complex other); operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*=(const scalar_operator &other); - operator_sum operator+=(const scalar_operator &other); - operator_sum operator-=(const scalar_operator &other); + operator_sum& operator*=(const scalar_operator &other); + operator_sum& operator+=(const scalar_operator &other); + operator_sum& operator-=(const scalar_operator &other); operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum operator*=(const HandlerTy &other); - operator_sum operator+=(const HandlerTy &other); - operator_sum operator-=(const HandlerTy &other); + operator_sum& operator*=(const HandlerTy &other); + operator_sum& operator+=(const HandlerTy &other); + operator_sum& operator-=(const HandlerTy &other); operator_sum operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - operator_sum operator*=(const product_operator &other); - operator_sum operator+=(const product_operator &other); - operator_sum operator-=(const product_operator &other); + operator_sum& operator*=(const product_operator &other); + operator_sum& operator+=(const product_operator &other); + operator_sum& operator-=(const product_operator &other); operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; - operator_sum operator*=(const operator_sum &other); - operator_sum operator+=(const operator_sum &other); - operator_sum operator-=(const operator_sum &other); + operator_sum& operator*=(const operator_sum &other); + operator_sum& operator+=(const operator_sum &other); + operator_sum& operator-=(const operator_sum &other); #ifdef __clang__ #pragma clang diagnostic push @@ -201,7 +201,7 @@ class product_operator : public operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() const { + std::vector> get_operators() const { return ops; }; @@ -223,23 +223,23 @@ class product_operator : public operator_sum { product_operator operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - product_operator operator*=(double other); + product_operator& operator*=(double other); product_operator operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - product_operator operator*=(std::complex other); + product_operator& operator*=(std::complex other); product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - product_operator operator*=(const scalar_operator &other); + product_operator& operator*=(const scalar_operator &other); product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator operator*=(const HandlerTy &other); + product_operator& operator*=(const HandlerTy &other); product_operator operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - product_operator operator*=(const product_operator &other); + product_operator& operator*=(const product_operator &other); operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; @@ -341,26 +341,26 @@ class scalar_operator { scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; scalar_operator operator-(double other) const; - void operator*=(double other); - void operator/=(double other); - void operator+=(double other); - void operator-=(double other); + scalar_operator& operator*=(double other); + scalar_operator& operator/=(double other); + scalar_operator& operator+=(double other); + scalar_operator& operator-=(double other); scalar_operator operator*(std::complex other) const; scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - void operator*=(std::complex other); - void operator/=(std::complex other); - void operator+=(std::complex other); - void operator-=(std::complex other); + scalar_operator& operator*=(std::complex other); + scalar_operator& operator/=(std::complex other); + scalar_operator& operator+=(std::complex other); + scalar_operator& operator-=(std::complex other); scalar_operator operator*(const scalar_operator &other) const; scalar_operator operator/(const scalar_operator &other) const; scalar_operator operator+(const scalar_operator &other) const; scalar_operator operator-(const scalar_operator &other) const; - void operator*=(const scalar_operator &other); - void operator/=(const scalar_operator &other); - void operator+=(const scalar_operator &other); - void operator-=(const scalar_operator &other); + scalar_operator& operator*=(const scalar_operator &other); + scalar_operator& operator/=(const scalar_operator &other); + scalar_operator& operator+=(const scalar_operator &other); + scalar_operator& operator-=(const scalar_operator &other); /// TODO: implement and test pow friend scalar_operator operator*(double other, const scalar_operator &self); @@ -556,5 +556,12 @@ class rydberg_hamiltonian : public operator_sum { scalar_operator delta_global; std::optional>> delta_local; }; +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +template class product_operator; +template class operator_sum; +#else +extern template class product_operator; +extern template class operator_sum; +#endif } // namespace cudaq \ No newline at end of file From 6d3e4bf661a83846b30fbbe0fd81180bf6395261 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 16 Jan 2025 13:02:59 +0000 Subject: [PATCH 171/311] fixing a some types Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 18 +++++++++--------- runtime/cudaq/operators.h | 12 ++++++------ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 0fcf5fe70a..4922562910 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -192,25 +192,25 @@ operator_sum operator-(const scalar_operator &other, const operator_s } template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; auto selfOpSum = operator_sum(_prods); return selfOpSum * self; } template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; auto selfOpSum = operator_sum(_prods); return selfOpSum + self; } template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; auto selfOpSum = operator_sum(_prods); return selfOpSum - self; } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d356c03598..5de4c7b586 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -139,9 +139,9 @@ class operator_sum { friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); + friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif @@ -261,9 +261,9 @@ class product_operator : public operator_sum { friend product_operator operator*(const scalar_operator &other, const product_operator &self); friend operator_sum operator+(const scalar_operator &other, const product_operator &self); friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - friend product_operator operator*(const HandlerTy &other, const product_operator &self); - friend operator_sum operator+(const HandlerTy &other, const product_operator &self); - friend operator_sum operator-(const HandlerTy &other, const product_operator &self); + friend product_operator operator*(const HandlerTy &other, const product_operator &self); + friend operator_sum operator+(const HandlerTy &other, const product_operator &self); + friend operator_sum operator-(const HandlerTy &other, const product_operator &self); #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif From ccc5cc2804eeeca5ed3f0e92c064c47c0e354c68 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 16 Jan 2025 18:19:17 +0000 Subject: [PATCH 172/311] this builds, but needs to be cleaned up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 13 +- runtime/cudaq/dynamics/instantiations.cpp | 815 +++++++++++++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 320 -------- runtime/cudaq/dynamics/product_operators.cpp | 218 ----- runtime/cudaq/operators.h | 133 +++ 5 files changed, 955 insertions(+), 544 deletions(-) create mode 100644 runtime/cudaq/dynamics/instantiations.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 3818446488..3bbfb4eec0 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,18 +11,19 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - scalar_operators.cpp - elementary_operators.cpp - product_operators.cpp - operator_sum.cpp - schedule.cpp - definition.cpp helpers.cpp rydberg_hamiltonian.cpp cudm_helpers.cpp cudm_state.cpp cudm_time_stepper.cpp runge_kutta_integrator.cpp + definition.cpp + scalar_operators.cpp + elementary_operators.cpp + product_operators.cpp + operator_sum.cpp + instantiations.cpp + schedule.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/instantiations.cpp new file mode 100644 index 0000000000..71add82c27 --- /dev/null +++ b/runtime/cudaq/dynamics/instantiations.cpp @@ -0,0 +1,815 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include +#include +#include +#include +#include +#include +#include + +namespace cudaq { + +// product operator left-hand arithmetics + +template +requires std::derived_from +product_operator operator*(const scalar_operator &other, const product_operator &self) { + std::vector> terms = + self.get_operators(); + /// Insert this scalar operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); +} + +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const product_operator &self) { + return operator_sum({product_operator({other}), self * (-1.)}); +} + +template +requires std::derived_from +product_operator operator*(double other, const product_operator &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(double other, const product_operator &self) { + return operator_sum({product_operator({scalar_operator(other)}), self}); +} + +template +requires std::derived_from +operator_sum operator-(double other, const product_operator &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +product_operator operator*(const std::complex other, const product_operator &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(const std::complex other, const product_operator &self) { + return operator_sum({product_operator({scalar_operator(other)}), self}); +} + +template +requires std::derived_from +operator_sum operator-(const std::complex other, const product_operator &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector> terms = + self.get_operators(); + /// Insert this elementary operator to the front of the terms list. + terms.insert(terms.begin(), other); + return product_operator(terms); +} + +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + return operator_sum({product_operator({other}), self}); +} + +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return (self * (-1.)) + other; +} + +// operator sum left-hand arithmetics + +template +requires std::derived_from +operator_sum operator*(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + for (auto &term : terms) + term = term * other; + return operator_sum(terms); +} + +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const operator_sum &self) { + std::vector> terms = self.get_terms(); + terms.insert(terms.begin(), product_operator({other})); + return operator_sum(terms); +} + +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const operator_sum &self) { + auto negative_self = self * (-1.); + std::vector> terms = negative_self.get_terms(); + terms.insert(terms.begin(), product_operator({other})); + return operator_sum(terms); +} + +template +requires std::derived_from +operator_sum operator*(double other, const operator_sum &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(double other, const operator_sum &self) { + return self + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator-(double other, const operator_sum &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator*(std::complex other, const operator_sum &self) { + return self * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator+(std::complex other, const operator_sum &self) { + return self + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator-(std::complex other, const operator_sum &self) { + return (self * (-1.)) + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum * self; +} + +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum + self; +} + +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const operator_sum &self) { + std::vector> new_term = {other}; + std::vector> _prods = {product_operator(new_term)}; + auto selfOpSum = operator_sum(_prods); + return selfOpSum - self; +} + +// operator sum right-hand arithmetics + +template +requires std::derived_from +operator_sum operator_sum::operator*(double other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(double other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(double other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(double other) { + *this *= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(double other) { + *this += scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(double other) { + *this -= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(std::complex other) { + *this *= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(std::complex other) { + *this += scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(std::complex other) { + *this -= scalar_operator(other); + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const scalar_operator &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const scalar_operator &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const scalar_operator &other) const { + return *this + (other * (-1.0)); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const scalar_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const scalar_operator &other) { + *this = *this + other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const scalar_operator &other) { + *this = *this - other; + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const HandlerTy &other) const { + std::vector> combined_terms = terms; + std::vector> _other = { + other}; + combined_terms.push_back(product_operator(_other)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const HandlerTy &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other * (-1.)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const HandlerTy &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this + product_operator(_other); + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const HandlerTy &other) { + std::vector> _other = { + other}; + *this = *this - product_operator(_other); + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector> combined_terms = terms; + for (auto &term : combined_terms) { + term *= other; + } + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const product_operator &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const product_operator &other) const { + std::vector> combined_terms = terms; + combined_terms.push_back(other * (-1.)); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const product_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const product_operator &other) { + *this = *this + other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const product_operator &other) { + *this = *this - other; + return *this; +} + +template +requires std::derived_from +operator_sum operator_sum::operator*(const operator_sum &other) const { + auto self_terms = terms; + std::vector> product_terms; + auto other_terms = other.get_terms(); + for (auto &term : self_terms) { + for (auto &other_term : other_terms) { + product_terms.push_back(term * other_term); + } + } + return operator_sum(product_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator+(const operator_sum &other) const { + std::vector> combined_terms = terms; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.terms.begin()), + std::make_move_iterator(other.terms.end())); + return operator_sum(combined_terms); +} + +template +requires std::derived_from +operator_sum operator_sum::operator-(const operator_sum &other) const { + return *this + (other * (-1)); +} + +template +requires std::derived_from +operator_sum& operator_sum::operator*=(const operator_sum &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator-=(const operator_sum &other) { + *this = *this - other; + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator+=(const operator_sum &other) { + *this = *this + other; + return *this; +} + + +// product operator right-hand arithmetics + +template +requires std::derived_from +product_operator product_operator::operator*(double other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(double other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(double other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(double other) { + *this = *this * scalar_operator(other); + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(std::complex other) const { + return *this * scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(std::complex other) const { + return *this + scalar_operator(other); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(std::complex other) const { + return *this - scalar_operator(other); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(std::complex other) { + *this = *this * scalar_operator(other); + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(const scalar_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const scalar_operator &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const scalar_operator &other) const { + return operator_sum({*this, product_operator({other * (-1.)})}); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(const scalar_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(const HandlerTy &other) const { + std::vector> + combined_terms = ops; + combined_terms.push_back(other); + return product_operator(combined_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const HandlerTy &other) const { + std::vector> _other = { + other}; + return operator_sum({*this, product_operator(_other)}); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const HandlerTy &other) const { + return operator_sum({*this, other * (-1.)}); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(const HandlerTy &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +product_operator product_operator::operator*(const product_operator &other) const { + std::vector> + combined_terms = ops; + combined_terms.insert(combined_terms.end(), + std::make_move_iterator(other.ops.begin()), + std::make_move_iterator(other.ops.end())); + return product_operator(combined_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const product_operator &other) const { + return operator_sum({*this, other}); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const product_operator &other) const { + std::vector> combined_terms = {*this, other * (-1.)}; + return operator_sum(combined_terms); +} + +template +requires std::derived_from +product_operator& product_operator::operator*=(const product_operator &other) { + *this = *this * other; + return *this; +} + +template +requires std::derived_from +operator_sum product_operator::operator*(const operator_sum &other) const { + std::vector> other_terms = other.get_terms(); + for (auto &term : other_terms) { + term = *this * term; + } + return operator_sum(other_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator+(const operator_sum &other) const { + std::vector other_terms = other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + +template +requires std::derived_from +operator_sum product_operator::operator-(const operator_sum &other) const { + auto negative_other = other * (-1.); + std::vector> other_terms = negative_other.get_terms(); + other_terms.insert(other_terms.begin(), *this); + return operator_sum(other_terms); +} + + +// instantiations + +template +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); +template +product_operator operator*(const elementary_operator &other, const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator+(std::complex other, const product_operator &self); +template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +operator_sum operator-(std::complex other, const product_operator &self); +template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); +template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); +template +operator_sum operator-(std::complex other, const operator_sum &self); +template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); + +template +operator_sum operator_sum::operator*(double other) const; +template +operator_sum operator_sum::operator+(double other) const; +template +operator_sum operator_sum::operator-(double other) const; +template +operator_sum& operator_sum::operator*=(double other); +template +operator_sum& operator_sum::operator+=(double other); +template +operator_sum& operator_sum::operator-=(double other); +template +operator_sum operator_sum::operator*(std::complex other) const; +template +operator_sum operator_sum::operator+(std::complex other) const; +template +operator_sum operator_sum::operator-(std::complex other) const; +template +operator_sum& operator_sum::operator*=(std::complex other); +template +operator_sum& operator_sum::operator+=(std::complex other); +template +operator_sum& operator_sum::operator-=(std::complex other); +template +operator_sum operator_sum::operator*(const scalar_operator &other) const; +template +operator_sum operator_sum::operator+(const scalar_operator &other) const; +template +operator_sum operator_sum::operator-(const scalar_operator &other) const; +template +operator_sum& operator_sum::operator*=(const scalar_operator &other); +template +operator_sum& operator_sum::operator+=(const scalar_operator &other); +template +operator_sum& operator_sum::operator-=(const scalar_operator &other); +template +operator_sum operator_sum::operator*(const elementary_operator &other) const; +template +operator_sum operator_sum::operator+(const elementary_operator &other) const; +template +operator_sum operator_sum::operator-(const elementary_operator &other) const; +template +operator_sum& operator_sum::operator*=(const elementary_operator &other); +template +operator_sum& operator_sum::operator+=(const elementary_operator &other); +template +operator_sum& operator_sum::operator-=(const elementary_operator &other); +template +operator_sum operator_sum::operator*(const product_operator &other) const; +template +operator_sum operator_sum::operator+(const product_operator &other) const; +template +operator_sum operator_sum::operator-(const product_operator &other) const; +template +operator_sum& operator_sum::operator*=(const product_operator &other); +template +operator_sum& operator_sum::operator+=(const product_operator &other); +template +operator_sum& operator_sum::operator-=(const product_operator &other); +template +operator_sum operator_sum::operator*(const operator_sum &other) const; +template +operator_sum operator_sum::operator+(const operator_sum &other) const; +template +operator_sum operator_sum::operator-(const operator_sum &other) const; +template +operator_sum& operator_sum::operator*=(const operator_sum &other); +template +operator_sum& operator_sum::operator-=(const operator_sum &other); +template +operator_sum& operator_sum::operator+=(const operator_sum &other); + +template +product_operator product_operator::operator*(double other) const; +template +operator_sum product_operator::operator+(double other) const; +template +operator_sum product_operator::operator-(double other) const; +template +product_operator& product_operator::operator*=(double other); +template +product_operator product_operator::operator*(std::complex other) const; +template +operator_sum product_operator::operator+(std::complex other) const; +template +operator_sum product_operator::operator-(std::complex other) const; +template +product_operator& product_operator::operator*=(std::complex other); +template +product_operator product_operator::operator*(const scalar_operator &other) const; +template +operator_sum product_operator::operator+(const scalar_operator &other) const; +template +operator_sum product_operator::operator-(const scalar_operator &other) const; +template +product_operator& product_operator::operator*=(const scalar_operator &other); +template +product_operator product_operator::operator*(const elementary_operator &other) const; +template +operator_sum product_operator::operator+(const elementary_operator &other) const; +template +operator_sum product_operator::operator-(const elementary_operator &other) const; +template +product_operator& product_operator::operator*=(const elementary_operator &other); +template +product_operator product_operator::operator*(const product_operator &other) const; +template +operator_sum product_operator::operator+(const product_operator &other) const; +template +operator_sum product_operator::operator-(const product_operator &other) const; +template +product_operator& product_operator::operator*=(const product_operator &other); +template +operator_sum product_operator::operator*(const operator_sum &other) const; +template +operator_sum product_operator::operator+(const operator_sum &other) const; +template +operator_sum product_operator::operator-(const operator_sum &other) const; + +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 4922562910..f2c7b75311 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -136,325 +136,5 @@ namespace cudaq { // return result; // } -// left-hand arithmetics - -template -operator_sum operator*(double other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(double other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(std::complex other, const operator_sum &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(std::complex other, const operator_sum &self) { - return scalar_operator(other) + self; -} - -template -operator_sum operator-(std::complex other, const operator_sum &self) { - return scalar_operator(other) - self; -} - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - for (auto &term : terms) - term = other * term; - return operator_sum(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self) { - auto negative_self = (-1. * self); - std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), other); - return operator_sum(terms); -} - -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * self; -} - -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + self; -} - -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - self; -} - -// right-hand arithmetics - -template -operator_sum operator_sum::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(double other) const { - return *this - scalar_operator(other); -} - -template -operator_sum& operator_sum::operator*=(double other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(double other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(double other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum operator_sum::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - -template -operator_sum& operator_sum::operator*=(std::complex other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(std::complex other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(std::complex other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const scalar_operator &other) const { - return *this + (-1.0 * other); -} - -template -operator_sum& operator_sum::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const scalar_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const scalar_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back((-1. * other)); - return operator_sum(combined_terms); -} - -template -operator_sum& operator_sum::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this + product_operator(_other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = { - other}; - *this = *this - product_operator(_other); - return *this; -} - -template -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = terms; - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back(other); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const product_operator &other) const { - std::vector> combined_terms = terms; - combined_terms.push_back(other * (-1.)); - return operator_sum(combined_terms); -} - -template -operator_sum& operator_sum::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const product_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const product_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = terms; - std::vector> product_terms; - auto other_terms = other.get_terms(); - for (auto &term : self_terms) { - for (auto &other_term : other_terms) { - product_terms.push_back(term * other_term); - } - } - return operator_sum(product_terms); -} - -template -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = terms; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.terms.begin()), - std::make_move_iterator(other.terms.end())); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (other * (-1)); -} - -template -operator_sum& operator_sum::operator*=(const operator_sum &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const operator_sum &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const operator_sum &other) { - *this = *this + other; - return *this; -} } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index af83948370..1e9205c592 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -161,222 +161,4 @@ std::vector product_operator::degrees() const { return std::vector(unique_degrees.begin(), unique_degrees.end()); } -// left-hand arithmetics - -template -product_operator operator*(double other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(double other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(double other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const std::complex other, const product_operator &self) { - return scalar_operator(other) * self; -} - -template -operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum({scalar_operator(other), self}); -} - -template -operator_sum operator-(const std::complex other, const product_operator &self) { - return scalar_operator(other) - self; -} - -template -product_operator operator*(const scalar_operator &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this scalar operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); -} - -template -operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), (-1. * self)}); -} - -template -product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this elementary operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); -} - -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - std::vector> new_term = {other}; - return operator_sum({product_operator(new_term), self}); -} - -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return other + (-1. * self); -} - -// right-hand arithmetics - -template -product_operator product_operator::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(double other) const { - return *this - scalar_operator(other); -} - -template -product_operator& product_operator::operator*=(double other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator product_operator::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - -template -product_operator& product_operator::operator*=(std::complex other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator product_operator::operator*(const scalar_operator &other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); -} - -template -operator_sum product_operator::operator+(const scalar_operator &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); -} - -template -operator_sum product_operator::operator-(const scalar_operator &other) const { - return operator_sum({*this, product_operator({-1. * other})}); -} - -template -product_operator& product_operator::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -product_operator product_operator::operator*(const HandlerTy &other) const { - std::vector> - combined_terms = ops; - combined_terms.push_back(other); - return product_operator(combined_terms); -} - -template -operator_sum product_operator::operator+(const HandlerTy &other) const { - std::vector> _other = { - other}; - return operator_sum({*this, product_operator(_other)}); -} - -template -operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum({*this, -1. * other}); -} - -template -product_operator& product_operator::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - -template -product_operator product_operator::operator*(const product_operator &other) const { - std::vector> - combined_terms = ops; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.ops.begin()), - std::make_move_iterator(other.ops.end())); - return product_operator(combined_terms); -} - -template -operator_sum product_operator::operator+(const product_operator &other) const { - return operator_sum({*this, other}); -} - -template -operator_sum product_operator::operator-(const product_operator &other) const { - std::vector> combined_terms = {*this, -1. * other}; - return operator_sum(combined_terms); -} - -template -product_operator& product_operator::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum product_operator::operator*(const operator_sum &other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) { - term = *this * term; - } - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator+(const operator_sum &other) const { - std::vector other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator-(const operator_sum &other) const { - auto negative_other = (-1. * other); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 5de4c7b586..d5928ff743 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -31,6 +31,135 @@ template requires std::derived_from class operator_sum; +template +requires std::derived_from +product_operator operator*(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(double other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const product_operator &self); + +template +requires std::derived_from +operator_sum operator*(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const operator_sum &self); + + +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +#else +extern template +product_operator operator*(double other, const product_operator &self); +extern template +operator_sum operator+(double other, const product_operator &self); +extern template +operator_sum operator-(double other, const product_operator &self); +extern template +product_operator operator*(std::complex other, const product_operator &self); +extern template +operator_sum operator+(std::complex other, const product_operator &self); +extern template +operator_sum operator-(std::complex other, const product_operator &self); +extern template +product_operator operator*(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +extern template +product_operator operator*(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + +extern template +operator_sum operator*(double other, const operator_sum &self); +extern template +operator_sum operator+(double other, const operator_sum &self); +extern template +operator_sum operator-(double other, const operator_sum &self); +extern template +operator_sum operator*(std::complex other, const operator_sum &self); +extern template +operator_sum operator+(std::complex other, const operator_sum &self); +extern template +operator_sum operator-(std::complex other, const operator_sum &self); +extern template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); +#endif + + /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -130,6 +259,7 @@ class operator_sum { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-template-friend" #endif +/* friend operator_sum operator*(double other, const operator_sum &self); friend operator_sum operator+(double other, const operator_sum &self); friend operator_sum operator-(double other, const operator_sum &self); @@ -142,6 +272,7 @@ class operator_sum { friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); +*/ #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif @@ -252,6 +383,7 @@ class product_operator : public operator_sum { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnon-template-friend" #endif +/* friend product_operator operator*(double other, const product_operator &self); friend operator_sum operator+(double other, const product_operator &self); friend operator_sum operator-(double other, const product_operator &self); @@ -264,6 +396,7 @@ class product_operator : public operator_sum { friend product_operator operator*(const HandlerTy &other, const product_operator &self); friend operator_sum operator+(const HandlerTy &other, const product_operator &self); friend operator_sum operator-(const HandlerTy &other, const product_operator &self); +*/ #if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) #pragma GCC diagnostic pop #endif From 1a909db3c9d2f82273cdb9f1664928cf66fba1e3 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 17 Jan 2025 13:23:19 +0000 Subject: [PATCH 173/311] scalar ops tests Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/scalar_operators.cpp | 18 +++++++++--------- unittests/dynamics/scalar_ops_arithmetic.cpp | 13 +++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 24ce886a65..7eacac4775 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -55,7 +55,7 @@ matrix_2 scalar_operator::to_matrix( #define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ scalar_operator operator op(double other, const scalar_operator &self) { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -70,7 +70,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); scalar_operator operator op(std::complex other, \ const scalar_operator &self) { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -86,7 +86,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ scalar_operator scalar_operator::operator op(double other) const { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -103,7 +103,7 @@ ARITHMETIC_OPERATIONS_DOUBLES(-); * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ this->generator = ScalarCallbackFunction(newGenerator); \ @@ -117,9 +117,9 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ scalar_operator scalar_operator::operator op( \ - const std::complex other) const{ \ + std::complex other) const{ \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -137,7 +137,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ this->generator = ScalarCallbackFunction(newGenerator); \ @@ -153,7 +153,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); scalar_operator scalar_operator::operator op( \ const scalar_operator &other) const { \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(ScalarCallbackFunction(newGenerator)); \ @@ -171,7 +171,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); * that we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ auto newGenerator = \ - [&](std::map> parameters) { \ + [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ }; \ this->generator = ScalarCallbackFunction(newGenerator); \ diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index edd4f9e675..00f93c1fc8 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -31,6 +31,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { auto new_scalar_op = value_1 + scalar_op; // function + scalar_op; auto reverse_order_op = scalar_op + value_1; + EXPECT_NEAR(std::abs(scalar_op.evaluate({})), std::abs(value_0), 1e-5); auto got_value = new_scalar_op.evaluate({}); auto got_value_1 = reverse_order_op.evaluate({}); @@ -156,13 +157,13 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { auto got_value = new_scalar_op.evaluate({}); auto got_value_1 = reverse_order_op.evaluate({}); - EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); - EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_2), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 / value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; auto got_value_third = third_op.evaluate({}); - auto want_value = (value_2 / value_3) / (value_3 / value_2); + auto want_value = (value_3 / value_2) / (value_2 / value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -176,13 +177,13 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { auto got_value = new_scalar_op.evaluate({{"value", value_1}}); auto got_value_1 = reverse_order_op.evaluate({{"value", value_1}}); - EXPECT_NEAR(std::abs(got_value), std::abs(value_1 / value_3), 1e-5); - EXPECT_NEAR(std::abs(got_value_1), std::abs(value_3 / value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_1), 1e-5); + EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 / value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; auto got_value_third = third_op.evaluate({{"value", value_1}}); - auto want_value = (value_1 / value_3) / (value_3 / value_1); + auto want_value = (value_3 / value_1) / (value_1 / value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } From 314d61fb370b91a8e902d859b726c8f9aaa391fb Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 23 Jan 2025 16:35:21 +0000 Subject: [PATCH 174/311] to be deleted again - just some notes Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/memory.cpp | 106 ++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 runtime/cudaq/dynamics/memory.cpp diff --git a/runtime/cudaq/dynamics/memory.cpp b/runtime/cudaq/dynamics/memory.cpp new file mode 100644 index 0000000000..ffb0bfeaf2 --- /dev/null +++ b/runtime/cudaq/dynamics/memory.cpp @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// FILE TO BE DELETED - THERE ARE JUST SOME NOTES/EXPERIMENTS TO CHECK HOW TO AVOID UNNECESSARY COPIES + +#include +#include +#include +#include // enable_if, conjuction + +template +using are_same = std::conjunction...>; + +class Foo { +public: + int i; + Foo() = default; + Foo(int i) : i(i) {} + Foo(const Foo &other) : i(other.i) { + std::cout << "copy Foo" << std::endl; + } +}; + +class Baz { +public: + Foo f; + Baz(const Foo &foo) : f(foo) {} + + Baz(const Baz& other) : f(other.f) { + std::cout << "copy Baz" << std::endl; + } +}; + +class Bar { +private: + + void aggregate(const Baz& head) { + std::cout << "got last " << head.f.i << std::endl; + items.push_back(head.f); + } + + template + void aggregate(const Baz &head, Args&& ... args) + { + std::cout << "got " << head.f.i << std::endl; + items.push_back(head.f); + aggregate(std::forward(args)...); + } + +public: + std::vector items; + Bar() = default; + Bar(const Foo &foo) { + items.reserve(1); + items.push_back(foo); + } + Bar(const Bar &other) : items(other.items) { + std::cout << "copy Bar" << std::endl; + } + //Bar(std::initializer_list args) : items(args) {} + + Bar& operator=(const Bar& other) { + std::cout << "assignment Bar" << std::endl; + // Check for self-assignment + if (this != &other) { + items = other.items; + //auto dummy = other.items; + //other.items; + } + return *this; + } + + template::value, void>> + Bar(const Args&... args) { + items.reserve(sizeof...(Args)); + std::cout << "create Bar from Baz" << std::endl; + aggregate(args...); + std::cout << "done" << std::endl; + } +}; + +int main() +{ + Bar bar; + { + Foo foo(5); + Bar dummy(foo); // creates 1 copy of foo + std::cout << dummy.items[0].i << std::endl; + bar = Bar(foo); // creates 1 copy to construct bar, 1 copy when assigning + } + std::cout << bar.items[0].i << std::endl; + //std::cout << foo.i << std::endl; + + Baz op1(Foo(1)); + Baz op2(Foo(2)); + + Bar bar2(op1, op2); + std::cout << bar2.items[0].i << " " << bar2.items[1].i << std::endl; + + return 0; +} \ No newline at end of file From b3e2edd30ff84b21308d12b7520ac4a609b7754b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 24 Jan 2025 11:29:23 +0000 Subject: [PATCH 175/311] more memory experiments Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/memory.cpp | 131 ++++++++++++++++++++++++++++-- 1 file changed, 123 insertions(+), 8 deletions(-) diff --git a/runtime/cudaq/dynamics/memory.cpp b/runtime/cudaq/dynamics/memory.cpp index ffb0bfeaf2..36c713a114 100644 --- a/runtime/cudaq/dynamics/memory.cpp +++ b/runtime/cudaq/dynamics/memory.cpp @@ -13,9 +13,6 @@ #include #include // enable_if, conjuction -template -using are_same = std::conjunction...>; - class Foo { public: int i; @@ -24,12 +21,38 @@ class Foo { Foo(const Foo &other) : i(other.i) { std::cout << "copy Foo" << std::endl; } + + Foo(Foo &&other) { + *this = std::forward(other); + } + + Foo& operator=(const Foo& other) { + std::cout << "assignment Foo" << std::endl; + // Check for self-assignment + if (this != &other) { + i = other.i; + } + return *this; + } + + Foo& operator=(Foo&& other) noexcept { + std::cout << "move Foo" << std::endl; + this->i = other.i; + return *this; + } }; class Baz { public: Foo f; - Baz(const Foo &foo) : f(foo) {} + Baz(Foo&& foo) { + std::cout << "create Baz" << std::endl; + f = std::move(foo); + } + + Baz(const Foo &foo) : f(foo) { + std::cout << "create Baz" << std::endl; + } Baz(const Baz& other) : f(other.f) { std::cout << "copy Baz" << std::endl; @@ -55,27 +78,42 @@ class Bar { public: std::vector items; Bar() = default; + + Bar(Foo&& foo) { + std::cout << "create Bar from &&" << std::endl; + items.push_back(std::forward(foo)); + } + + Bar(Bar&& other) { + items = std::move(other.items); + } + Bar(const Foo &foo) { - items.reserve(1); items.push_back(foo); } + Bar(const Bar &other) : items(other.items) { std::cout << "copy Bar" << std::endl; } + //Bar(std::initializer_list args) : items(args) {} + Bar& operator=(Bar&& other) noexcept { + std::cout << "move Bar" << std::endl; + this->items = std::forward>(other.items); + return *this; + } + Bar& operator=(const Bar& other) { std::cout << "assignment Bar" << std::endl; // Check for self-assignment if (this != &other) { items = other.items; - //auto dummy = other.items; - //other.items; } return *this; } - template::value, void>> + template...>::value, void>> Bar(const Args&... args) { items.reserve(sizeof...(Args)); std::cout << "create Bar from Baz" << std::endl; @@ -84,6 +122,41 @@ class Bar { } }; +class Dummy1 { +protected: + std::vector> terms; + Dummy1() = default; +public: + Dummy1(std::vector> data) : terms(data) {} + + std::vector get(int i) { + return terms[i]; + } +}; + +class Dummy2 : public Dummy1 { +public: + Dummy2(std::vector data) : Dummy1({data}) {} + + std::string get(int i) { + return terms[0][i]; + } +}; + +class Dummy3 { +public: + std::vector> items; + Dummy3(const std::vector& ops) { + std::cout << "construct Dummy3" << std::endl; + items.push_back(ops); + } + + Dummy3(std::vector&& ops) { + std::cout << "construct Dummy3" << std::endl; + items.push_back(std::move(ops)); + } +}; + int main() { Bar bar; @@ -102,5 +175,47 @@ int main() Bar bar2(op1, op2); std::cout << bar2.items[0].i << " " << bar2.items[1].i << std::endl; + // FIXME: AVOID FOO COPY HERE + Bar bar3(Baz(Foo(3)), Baz(Foo(4))); + std::cout << bar3.items[0].i << " " << bar3.items[1].i << std::endl; + + //Bar(1, Dummy()); + + std::vector data1 = {"op1", "op2"}; + std::vector data2 = {"op3", "op4"}; + + Dummy1 d1({data1, data2}); + Dummy2 d2(data1); + + std::cout << d2.get(0) << " " << d2.get(1) << std::endl; + std::cout << ((Dummy1)d2).get(0)[0] << " " << ((Dummy1)d2).get(0)[1] << std::endl; + + std::cout << d1.get(0)[0] << " " << d1.get(0)[1] << std::endl; + std::cout << d1.get(1)[0] << " " << d1.get(1)[1] << std::endl; + + std::vector ops = {}; + ops.reserve(2); + { + ops.push_back(Bar(Foo(9))); + Bar op(Foo(10)); + ops.push_back(op); + } + + Dummy3 d3(std::move(ops)); + std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; + //std::cout << ops[0].items[0].i << std::endl; + + { + std::cout << std::endl; + // an initializer list will always make an extra copy (by design of the initializer list) + // d3 = Dummy3({Foo(11), Foo(12)}); -> this will make an extra copy... + std::vector ops2 = {}; + ops2.reserve(2); + ops2.push_back(Foo(11)); + ops2.push_back(Foo(12)); + d3 = Dummy3(std::move(ops2)); + } + std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; + return 0; } \ No newline at end of file From 86e13375cc50ca7c7ebfe0806676a1597bb1cdad Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 24 Jan 2025 14:18:27 +0000 Subject: [PATCH 176/311] revised the data structures - all tests now pass again Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 34 +-- runtime/cudaq/dynamics/instantiations.cpp | 68 +++-- runtime/cudaq/dynamics/product_operators.cpp | 2 +- runtime/cudaq/dynamics/scalar_operators.cpp | 53 ++-- runtime/cudaq/operators.h | 254 ++++++++++++++---- unittests/dynamics/scalar_ops_arithmetic.cpp | 2 +- 6 files changed, 280 insertions(+), 133 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 3f3805539c..e07bd900d1 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -15,22 +15,22 @@ namespace cudaq { +std::map elementary_operator::m_ops = {}; + elementary_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - int degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); // Build up the identity matrix. for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = 1.0 + 0.0 * 'j'; + mat[{i, i}] = 1.0 + 0.0j; } std::cout << "dumping the complex mat: \n"; @@ -292,14 +292,14 @@ operator_sum operator+(double other, const elementary_opera self}; std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum(product_operator(_other), product_operator(_self)); } operator_sum operator-(double other, const elementary_operator &self) { auto other_scalar = scalar_operator(other); std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), (-1. * self)}); + return operator_sum(product_operator(_other), (-1. * self)); } product_operator operator*(std::complex other, const elementary_operator &self) { @@ -315,12 +315,12 @@ operator_sum operator+(std::complex other, const el self}; std::vector> _other = { other_scalar}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum(product_operator(_other), product_operator(_self)); } operator_sum operator-(std::complex other, const elementary_operator &self) { std::vector> _other = {scalar_operator(other)}; - return operator_sum({product_operator(_other), (-1. * self)}); + return operator_sum(product_operator(_other), (-1. * self)); } product_operator operator*(const scalar_operator &other, const elementary_operator &self) { @@ -330,12 +330,12 @@ product_operator operator*(const scalar_operator &other, co operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { std::vector> _other = {other}; std::vector> _self = {self}; - return operator_sum({product_operator(_other), product_operator(_self)}); + return operator_sum(product_operator(_other), product_operator(_self)); } operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { std::vector> _other = {other}; - return operator_sum({product_operator(_other), -1. * self}); + return operator_sum(product_operator(_other), -1. * self); } // right-hand arithmetics @@ -370,7 +370,7 @@ operator_sum elementary_operator::operator+(std::complex> _other = { other_scalar}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } operator_sum elementary_operator::operator-(std::complex other) const { @@ -381,7 +381,7 @@ operator_sum elementary_operator::operator-(std::complex> _other = { other_scalar}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } product_operator elementary_operator::operator*(const scalar_operator &other) const { @@ -397,7 +397,7 @@ operator_sum elementary_operator::operator+(const scalar_op *this}; std::vector> _other = { other}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } operator_sum elementary_operator::operator-(const scalar_operator &other) const { @@ -407,7 +407,7 @@ operator_sum elementary_operator::operator-(const scalar_op *this}; std::vector> _other = { -1. * other}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } product_operator elementary_operator::operator*(const elementary_operator &other) const { @@ -421,13 +421,13 @@ operator_sum elementary_operator::operator+(const elementar *this}; std::vector> _other = { other}; - return operator_sum({product_operator(_this), product_operator(_other)}); + return operator_sum(product_operator(_this), product_operator(_other)); } operator_sum elementary_operator::operator-(const elementary_operator &other) const { std::vector> _this = { *this}; - return operator_sum({product_operator(_this), (-1. * other)}); + return operator_sum(product_operator(_this), (-1. * other)); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/instantiations.cpp index 71add82c27..52138d3ce3 100644 --- a/runtime/cudaq/dynamics/instantiations.cpp +++ b/runtime/cudaq/dynamics/instantiations.cpp @@ -32,13 +32,13 @@ product_operator operator*(const scalar_operator &other, const produc template requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); + return operator_sum(product_operator({other}), self); } template requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum({product_operator({other}), self * (-1.)}); + return operator_sum(product_operator({other}), self * (-1.)); } template @@ -50,7 +50,7 @@ product_operator operator*(double other, const product_operator requires std::derived_from operator_sum operator+(double other, const product_operator &self) { - return operator_sum({product_operator({scalar_operator(other)}), self}); + return operator_sum(product_operator({scalar_operator(other)}), self); } template @@ -68,7 +68,7 @@ product_operator operator*(const std::complex other, const pr template requires std::derived_from operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum({product_operator({scalar_operator(other)}), self}); + return operator_sum(product_operator({scalar_operator(other)}), self); } template @@ -90,7 +90,7 @@ product_operator operator*(const HandlerTy &other, const product_oper template requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum({product_operator({other}), self}); + return operator_sum(product_operator(other), self); } template @@ -273,7 +273,7 @@ operator_sum& operator_sum::operator-=(std::complex requires std::derived_from operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { term *= other; } @@ -283,9 +283,8 @@ operator_sum operator_sum::operator*(const scalar_operator template requires std::derived_from operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = terms; - std::vector> _other = { - other}; + std::vector> combined_terms = this->get_terms(); + std::vector> _other = {other}; combined_terms.push_back(product_operator(_other)); return operator_sum(combined_terms); } @@ -320,7 +319,7 @@ operator_sum& operator_sum::operator-=(const scalar_operat template requires std::derived_from operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { term *= other; } @@ -330,7 +329,7 @@ operator_sum operator_sum::operator*(const HandlerTy &othe template requires std::derived_from operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); std::vector> _other = { other}; combined_terms.push_back(product_operator(_other)); @@ -340,7 +339,7 @@ operator_sum operator_sum::operator+(const HandlerTy &othe template requires std::derived_from operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other * (-1.)); return operator_sum(combined_terms); } @@ -355,8 +354,7 @@ operator_sum& operator_sum::operator*=(const HandlerTy &ot template requires std::derived_from operator_sum& operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = { - other}; + std::vector> _other = {other}; *this = *this + product_operator(_other); return *this; } @@ -364,8 +362,7 @@ operator_sum& operator_sum::operator+=(const HandlerTy &ot template requires std::derived_from operator_sum& operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = { - other}; + std::vector> _other = {other}; *this = *this - product_operator(_other); return *this; } @@ -373,7 +370,7 @@ operator_sum& operator_sum::operator-=(const HandlerTy &ot template requires std::derived_from operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { term *= other; } @@ -383,7 +380,7 @@ operator_sum operator_sum::operator*(const product_operato template requires std::derived_from operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other); return operator_sum(combined_terms); } @@ -391,7 +388,7 @@ operator_sum operator_sum::operator+(const product_operato template requires std::derived_from operator_sum operator_sum::operator-(const product_operator &other) const { - std::vector> combined_terms = terms; + std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other * (-1.)); return operator_sum(combined_terms); } @@ -420,7 +417,7 @@ operator_sum& operator_sum::operator-=(const product_opera template requires std::derived_from operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = terms; + auto self_terms = this->get_terms(); std::vector> product_terms; auto other_terms = other.get_terms(); for (auto &term : self_terms) { @@ -434,10 +431,9 @@ operator_sum operator_sum::operator*(const operator_sum requires std::derived_from operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = terms; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.terms.begin()), - std::make_move_iterator(other.terms.end())); + std::vector> combined_terms = this->get_terms(); + // make_move_iterator for && overload + combined_terms.insert(combined_terms.end(), other.terms.begin(), other.terms.end()); return operator_sum(combined_terms); } @@ -525,7 +521,7 @@ template requires std::derived_from product_operator product_operator::operator*(const scalar_operator &other) const { std::vector> - combined_terms = ops; + combined_terms = operator_sum::terms[0]; combined_terms.push_back(other); return product_operator(combined_terms); } @@ -535,13 +531,14 @@ requires std::derived_from operator_sum product_operator::operator+(const scalar_operator &other) const { std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum(*this, product_operator(_other)); } template requires std::derived_from operator_sum product_operator::operator-(const scalar_operator &other) const { - return operator_sum({*this, product_operator({other * (-1.)})}); + std::vector> minus_term = {other * (-1.)}; + return operator_sum(*this, product_operator(minus_term)); } template @@ -555,7 +552,7 @@ template requires std::derived_from product_operator product_operator::operator*(const HandlerTy &other) const { std::vector> - combined_terms = ops; + combined_terms = operator_sum::terms[0]; combined_terms.push_back(other); return product_operator(combined_terms); } @@ -565,13 +562,13 @@ requires std::derived_from operator_sum product_operator::operator+(const HandlerTy &other) const { std::vector> _other = { other}; - return operator_sum({*this, product_operator(_other)}); + return operator_sum(*this, product_operator(_other)); } template requires std::derived_from operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum({*this, other * (-1.)}); + return operator_sum(*this, other * (-1.)); } template @@ -585,24 +582,21 @@ template requires std::derived_from product_operator product_operator::operator*(const product_operator &other) const { std::vector> - combined_terms = ops; - combined_terms.insert(combined_terms.end(), - std::make_move_iterator(other.ops.begin()), - std::make_move_iterator(other.ops.end())); + combined_terms = operator_sum::terms[0]; + combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); return product_operator(combined_terms); } template requires std::derived_from operator_sum product_operator::operator+(const product_operator &other) const { - return operator_sum({*this, other}); + return operator_sum(*this, other); } template requires std::derived_from operator_sum product_operator::operator-(const product_operator &other) const { - std::vector> combined_terms = {*this, other * (-1.)}; - return operator_sum(combined_terms); + return operator_sum(*this, other * (-1.)); } template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 1e9205c592..39a9e5d03f 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -149,7 +149,7 @@ std::vector product_operator::degrees() const { // The variant type makes it difficult auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; auto endFunc = [](auto &&t) { return t.degrees.end(); }; - for (const auto &term : ops) { + for (const auto &term : operator_sum::terms[0]) { unique_degrees.insert(std::visit(beginFunc, term), std::visit(endFunc, term)); } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 7eacac4775..c03e1f377b 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,32 +14,12 @@ namespace cudaq { -// constructors - -/// @brief Constructor that just takes and returns a complex double value. -scalar_operator::scalar_operator(std::complex value) { - m_constant_value = value; - auto func = [&](std::map> _none) { - return m_constant_value; - }; - generator = ScalarCallbackFunction(func); -} - -/// @brief Constructor that just takes a double and returns a complex double. -scalar_operator::scalar_operator(double value) { - std::complex castValue(value, 0.0); - m_constant_value = castValue; - auto func = [&](std::map> _none) { - return m_constant_value; - }; - generator = ScalarCallbackFunction(func); -} - // evaluations std::complex scalar_operator::evaluate( const std::map> parameters) const { - return generator(parameters); + if (m_constant_value.has_value()) return m_constant_value.value(); + else return generator(parameters); } matrix_2 scalar_operator::to_matrix( @@ -58,7 +38,7 @@ matrix_2 scalar_operator::to_matrix( [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); @@ -73,7 +53,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); [=](std::map> parameters) { \ return other op self.evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); @@ -89,7 +69,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_DOUBLES(*); @@ -99,6 +79,10 @@ ARITHMETIC_OPERATIONS_DOUBLES(-); #define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op(double other) { \ + if (this->m_constant_value.has_value()) { \ + this->m_constant_value.value() op other; \ + return *this; \ + } \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ @@ -106,7 +90,7 @@ ARITHMETIC_OPERATIONS_DOUBLES(-); [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ - this->generator = ScalarCallbackFunction(newGenerator); \ + this->generator = newGenerator; \ return *this; \ } @@ -122,7 +106,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); @@ -133,6 +117,10 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); #define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op( \ std::complex other) { \ + if (this->m_constant_value.has_value()) { \ + this->m_constant_value.value() op other; \ + return *this; \ + } \ /* Need to move the existing generating function to a new operator so that \ * we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ @@ -140,7 +128,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other; \ }; \ - this->generator = ScalarCallbackFunction(newGenerator); \ + this->generator = newGenerator; \ return *this; \ } @@ -156,7 +144,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); [=, this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ - return scalar_operator(ScalarCallbackFunction(newGenerator)); \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_SCALAR_OPS(*); @@ -167,6 +155,11 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op( \ const scalar_operator &other) { \ + if (this->m_constant_value.has_value() && \ + other.m_constant_value.has_value()) { \ + this->m_constant_value.value() op other.m_constant_value.value(); \ + return *this; \ + } \ /* Need to move the existing generating function to a new operator so \ * that we can modify the generator in-place. */ \ scalar_operator prevSelf(*this); \ @@ -174,7 +167,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); [=](std::map> parameters) { \ return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ }; \ - this->generator = ScalarCallbackFunction(newGenerator); \ + this->generator = newGenerator; \ return *this; \ } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d5928ff743..7e98629fd4 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -16,6 +16,7 @@ #include #include #include +#include namespace cudaq { @@ -169,21 +170,74 @@ requires std::derived_from class operator_sum { private: - std::vector> terms; - std::vector> canonicalize_product(product_operator &prod) const; std::vector> _canonical_terms() const; + void aggregate_terms(const product_operator& head) { + terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + } + + template + void aggregate_terms(const product_operator &head, Args&& ... args) { + terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + aggregate_terms(std::forward(args)...); + } + +protected: + operator_sum() = default; + std::vector>> terms; + public: + /// @brief Construct a `cudaq::operator_sum` given a sequence of /// `cudaq::product_operator`'s. /// This operator expression represents a sum of terms, where each term /// is a product of elementary and scalar operators. - operator_sum(const std::vector> &terms) - : terms(terms) {} + template, Args>...>::value, void>> + operator_sum(const Args&... args) { + std::cout << "op sum constructor" << std::endl; + terms.reserve(sizeof...(Args)); + aggregate_terms(args...); + } + + operator_sum(const std::vector>& terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(term.terms[0]); + } + } + + operator_sum(std::vector>&& terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(std::move(term.terms[0])); + } + } + + // copy constructor + operator_sum(const operator_sum &other) + : terms(other.terms) {} + + // move constructor + operator_sum(operator_sum &&other) + : terms(std::move(other.terms)) {} + + // assignment operator + operator_sum& operator=(const operator_sum& other) { + if (this != &other) { + terms = other.terms; + } + return *this; + } + + // move assignment operator + operator_sum& operator=(operator_sum &&other) { + terms = std::move(other.terms); + return *this; + } ~operator_sum() = default; @@ -296,7 +350,13 @@ class operator_sum { /// FIXME: Protect this once I can do deeper testing in `unittests`. // protected: - std::vector> get_terms() const { return terms; } + std::vector> get_terms() const { + std::vector> prods; + prods.reserve(terms.size()); + for (auto term : terms) { + prods.push_back(product_operator(term)); + } + return prods; } }; /// @brief Represents an operator expression consisting of a product of @@ -308,17 +368,64 @@ requires std::derived_from class product_operator : public operator_sum { private: - std::vector> ops; + + void aggregate_terms(const HandlerTy& head) { + operator_sum::terms[0].push_back(head); + } + + template + void aggregate_terms(const HandlerTy &head, Args&& ... args) { + operator_sum::terms[0].push_back(head); + aggregate_terms(std::forward(args)...); + } public: // Constructor for an operator expression that represents a product // of scalar and elementary operators. // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. - product_operator( - std::vector> - atomic_operators) - : operator_sum({*this}), ops(atomic_operators) {} + template...>::value, void>> + product_operator(const Args&... args) { + std::cout << "prod op constructor" << std::endl; + std::vector> ops = {}; + ops.reserve(sizeof...(Args)); + operator_sum::terms.push_back(ops); + aggregate_terms(args...); + } + + product_operator(const std::vector>& atomic_operators) { + std::cout << "prod op constructor" << std::endl; + operator_sum::terms.push_back(atomic_operators); + } + + product_operator(std::vector>&& atomic_operators) { + std::cout << "prod op constructor" << std::endl; + operator_sum::terms.push_back(std::move(atomic_operators)); + } + + // copy constructor + product_operator(const product_operator &other) { + operator_sum::terms = other.terms; + } + + // move constructor + product_operator(product_operator &&other) { + operator_sum::terms = std::move(other.terms); + } + + // assignment operator + product_operator& operator=(const product_operator& other) { + if (this != &other) { + operator_sum::terms = other.terms; + } + return *this; + } + + // move assignment operator + product_operator& operator=(product_operator &&other) { + operator_sum::terms = std::move(other.terms); + return *this; + } ~product_operator() = default; @@ -328,12 +435,11 @@ class product_operator : public operator_sum { /// @brief Return the number of operator terms that make up this product /// operator. - int term_count() const { return ops.size(); } + int term_count() const { return operator_sum::terms[0].size(); } - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: + /// FIXME: ELIMINATE THIS std::vector> get_operators() const { - return ops; + return operator_sum::terms[0]; }; /// @brief Return the `product_operator` as a string. @@ -425,21 +531,57 @@ class scalar_operator { private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. - std::complex m_constant_value; + std::optional> m_constant_value; + + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; public: + scalar_operator(double value) + : m_constant_value(value), generator() {} + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value) + : m_constant_value(value), generator() {} + + + scalar_operator(const ScalarCallbackFunction &create) + : m_constant_value(), generator(create) {} + /// @brief Constructor that just takes a callback function with no /// arguments. + scalar_operator(ScalarCallbackFunction &&create) + : m_constant_value() { + generator = std::move(create); + } - scalar_operator(ScalarCallbackFunction &&create) { - generator = ScalarCallbackFunction(create); + // copy constructor + scalar_operator(const scalar_operator &other) + : m_constant_value(other.m_constant_value), generator(other.generator) {} + + // move constructor + scalar_operator(scalar_operator &&other) + : m_constant_value(other.m_constant_value) { + generator = std::move(other.generator); } - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value); - scalar_operator(double value); + // assignment operator + scalar_operator& operator=(const scalar_operator &other) { + m_constant_value = other.m_constant_value; + generator = other.generator; + return *this; + } + + // move assignment operator + scalar_operator& operator=(scalar_operator &&other) { + m_constant_value = other.m_constant_value; + generator = std::move(other.generator); + return *this; + } /// NOTE: We should revisit these constructors and remove any that have /// become unnecessary as the implementation improves. @@ -450,11 +592,6 @@ class scalar_operator { ~scalar_operator() = default; - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - // Need this property for consistency with other inherited types. // Particularly, to be used when the scalar operator is held within // a variant type next to elementary operators. @@ -511,10 +648,18 @@ class scalar_operator { }; -class elementary_operator : public product_operator { +class elementary_operator { private: - std::map m_ops; + static std::map m_ops; + +protected: + // FIXME: revise implementation + /// @brief The number of levels, that is the dimension, for each degree of + /// freedom in canonical order that the operator acts on. A value of zero or + /// less indicates that the operator is defined for any dimension of that + /// degree. + std::map expected_dimensions; public: // The constructor should never be called directly by the user: @@ -523,28 +668,43 @@ class elementary_operator : public product_operator { /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - elementary_operator(std::string operator_id, std::vector degrees) - : id(operator_id), degrees(degrees), - product_operator({std::variant{*this}}) { } + elementary_operator(std::string operator_id, const std::vector °rees) + : id(operator_id), degrees(degrees) { + std::cout << "elem op constructor" << std::endl; + } + + // constructor + elementary_operator(std::string operator_id, std::vector &°rees) + : id(operator_id), degrees(std::move(degrees)) { + std::cout << "elem op constructor" << std::endl; + } - // Copy constructor. FIXME: NEEDED? + // copy constructor elementary_operator(const elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id), - product_operator({std::variant{*this}}) { } + : degrees(other.degrees), id(other.id) {} - elementary_operator(elementary_operator &other) - : m_ops(other.m_ops), expected_dimensions(other.expected_dimensions), - degrees(other.degrees), id(other.id), - product_operator({std::variant{*this}}) { } + // move constructor + elementary_operator(elementary_operator &&other) + : degrees(std::move(other.degrees)), id(other.id) {} + + // assignment operator + elementary_operator& operator=(const elementary_operator& other) { + if (this != &other) { + degrees = other.degrees; + id = other.id; + } + return *this; + } + + // move assignment operator + elementary_operator& operator=(elementary_operator &&other) { + degrees = std::move(other.degrees); + id = other.id; + return *this; + } ~elementary_operator() = default; - /// @brief The number of levels, that is the dimension, for each degree of - /// freedom in canonical order that the operator acts on. A value of zero or - /// less indicates that the operator is defined for any dimension of that - /// degree. - std::map expected_dimensions; /// @brief The degrees of freedom that the operator acts on in canonical /// order. std::vector degrees; @@ -634,13 +794,13 @@ class elementary_operator : public product_operator { template void define(std::string operator_id, std::map expected_dimensions, Func create) { - if (m_ops.find(operator_id) != m_ops.end()) { + if (elementary_operator::m_ops.find(operator_id) != elementary_operator::m_ops.end()) { // todo: make a nice error message to say op already exists throw; } auto defn = Definition(); defn.create_definition(operator_id, expected_dimensions, create); - m_ops[operator_id] = defn; + elementary_operator::m_ops[operator_id] = defn; } }; diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index 00f93c1fc8..b6d237eba4 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(OperatorExpressions, checkScalarOpsArithmeticDoubles) { +TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { // Arithmetic overloads against complex doubles. std::complex value_0 = 0.1 + 0.1; std::complex value_1 = 0.1 + 1.0; From 6908d91650c6bce985422b8e533f0352bb8bd2bc Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 27 Jan 2025 17:34:56 +0000 Subject: [PATCH 177/311] store one coefficient per product only Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 50 +- .../cudaq/dynamics/elementary_operators.cpp | 149 ++---- runtime/cudaq/dynamics/instantiations.cpp | 95 ++-- runtime/cudaq/dynamics/product_operators.cpp | 14 +- runtime/cudaq/dynamics/scalar_operators.cpp | 6 +- .../cudaq/dynamics/template_declarations.h | 154 ++++++ runtime/cudaq/operators.h | 455 +++++++----------- .../dynamics/elementary_ops_arithmetic.cpp | 19 +- unittests/dynamics/operator_sum.cpp | 34 +- .../dynamics/product_operators_arithmetic.cpp | 42 +- 10 files changed, 509 insertions(+), 509 deletions(-) create mode 100644 runtime/cudaq/dynamics/template_declarations.h diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index d5013ffc9c..d2760841ec 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -47,13 +47,30 @@ class CallbackFunction { _callback_func = std::forward(callable); } - // Copy constructor. - CallbackFunction(CallbackFunction &other) { + // copy constructor + CallbackFunction(const CallbackFunction &other) { _callback_func = other._callback_func; } - CallbackFunction(const CallbackFunction &other) { - _callback_func = other._callback_func; + // move constructor. + CallbackFunction(CallbackFunction &&other) { + _callback_func = std::move(other._callback_func); + } + + // assignment operator + CallbackFunction& operator=(const CallbackFunction &other) { + if (this != &other) { + _callback_func = other._callback_func; + } + return *this; + } + + // move assignment operator + CallbackFunction& operator=(CallbackFunction &&other) { + if (this != &other) { + _callback_func = std::move(other._callback_func); + } + return *this; } matrix_2 @@ -86,13 +103,30 @@ class ScalarCallbackFunction : CallbackFunction { _callback_func = std::forward(callable); } - // Copy constructor. - ScalarCallbackFunction(ScalarCallbackFunction &other) { + // copy constructor + ScalarCallbackFunction(const ScalarCallbackFunction &other) { _callback_func = other._callback_func; } - ScalarCallbackFunction(const ScalarCallbackFunction &other) { - _callback_func = other._callback_func; + // move constructor. + ScalarCallbackFunction(ScalarCallbackFunction &&other) { + _callback_func = std::move(other._callback_func); + } + + // assignment operator + ScalarCallbackFunction& operator=(const ScalarCallbackFunction &other) { + if (this != &other) { + _callback_func = other._callback_func; + } + return *this; + } + + // move assignment operator + ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other) { + if (this != &other) { + _callback_func = std::move(other._callback_func); + } + return *this; } bool operator!() { return (!_callback_func); } diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index e07bd900d1..b9b32e4682 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -32,10 +32,6 @@ elementary_operator elementary_operator::identity(int degree) { for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = 1.0 + 0.0j; } - - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -58,9 +54,6 @@ elementary_operator elementary_operator::zero(int degree) { auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -83,9 +76,6 @@ elementary_operator elementary_operator::annihilate(int degree) { for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -108,9 +98,6 @@ elementary_operator elementary_operator::create(int degree) { for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -137,9 +124,6 @@ elementary_operator elementary_operator::position(int degree) { mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -166,9 +150,6 @@ elementary_operator elementary_operator::momentum(int degree) { mat[{i, i + 1}] = -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -191,9 +172,6 @@ elementary_operator elementary_operator::number(int degree) { for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = static_cast(i) + 0.0j; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -216,9 +194,6 @@ elementary_operator elementary_operator::parity(int degree) { for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; } - std::cout << "dumping the complex mat: \n"; - std::cout << mat.dump(); - std::cout << "\ndone\n\n"; return mat; }; op.define(op_id, op.expected_dimensions, func); @@ -253,9 +228,6 @@ elementary_operator::displace(int degree, std::complex amplitude) { // // requires copies here. Maybe we can just use eigen directly here // // to limit to one copy, but we can address that later. // auto mat = temp_mat.exp(); - // std::cout << "dumping the complex mat: \n"; - // mat.dump(); - // std::cout << "\ndone\n\n"; // return mat; // }; // op.define(op_id, op.expected_dimensions, func); @@ -280,154 +252,103 @@ matrix_2 elementary_operator::to_matrix( // left-hand arithmetics product_operator operator*(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); + return product_operator(other, self); } operator_sum operator+(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_other), product_operator(_self)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., self)); } operator_sum operator-(double other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_other), (-1. * self)); + product_operator coefficient(other); + return operator_sum(coefficient, -1. * self); } product_operator operator*(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - other_scalar, self}; - return product_operator(_args); + return product_operator(other, self); } operator_sum operator+(std::complex other, const elementary_operator &self) { - auto other_scalar = scalar_operator(other); - std::vector> _self = { - self}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_other), product_operator(_self)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., self)); } operator_sum operator-(std::complex other, const elementary_operator &self) { - std::vector> _other = {scalar_operator(other)}; - return operator_sum(product_operator(_other), (-1. * self)); + product_operator coefficient(other); + return operator_sum(coefficient, -1. * self); } product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator({other, self}); + return product_operator(other, self); } operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - std::vector> _self = {self}; - return operator_sum(product_operator(_other), product_operator(_self)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., self)); } operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - std::vector> _other = {other}; - return operator_sum(product_operator(_other), -1. * self); + product_operator coefficient(other); + return operator_sum(coefficient, -1. * self); } // right-hand arithmetics product_operator elementary_operator::operator*(double other) const { - std::complex value(other, 0.0); - return *this * value; + return product_operator(other, *this); } operator_sum elementary_operator::operator+(double other) const { - std::complex value(other, 0.0); - return *this + value; + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., *this)); } operator_sum elementary_operator::operator-(double other) const { - std::complex value(other, 0.0); - return *this - value; + product_operator coefficient(-1. * other); + return operator_sum(coefficient, product_operator(1., *this)); } product_operator elementary_operator::operator*(std::complex other) const { - auto other_scalar = scalar_operator(other); - std::vector> _args = { - *this, other_scalar}; - return product_operator(_args); + return product_operator(other, *this); } operator_sum elementary_operator::operator+(std::complex other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator(other); - std::vector> _this = { - *this}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., *this)); } operator_sum elementary_operator::operator-(std::complex other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - auto other_scalar = scalar_operator((-1. * other)); - std::vector> _this = { - *this}; - std::vector> _other = { - other_scalar}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(-1. * other); + return operator_sum(coefficient, product_operator(1., *this)); } product_operator elementary_operator::operator*(const scalar_operator &other) const { - std::vector> _args = { - *this, other}; - return product_operator(_args); + return product_operator(other, *this); } operator_sum elementary_operator::operator+(const scalar_operator &other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - std::vector> _this = { - *this}; - std::vector> _other = { - other}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(other); + return operator_sum(coefficient, product_operator(1., *this)); } operator_sum elementary_operator::operator-(const scalar_operator &other) const { - // Operator sum is composed of product operators, so we must convert - // both underlying types to `product_operators` to perform the arithmetic. - std::vector> _this = { - *this}; - std::vector> _other = { - -1. * other}; - return operator_sum(product_operator(_this), product_operator(_other)); + product_operator coefficient(-1. * other); + return operator_sum(coefficient, product_operator(1., *this)); } product_operator elementary_operator::operator*(const elementary_operator &other) const { - std::vector> _args = { - *this, other}; - return product_operator(_args); + return product_operator(1., *this, other); } operator_sum elementary_operator::operator+(const elementary_operator &other) const { - std::vector> _this = { - *this}; - std::vector> _other = { - other}; - return operator_sum(product_operator(_this), product_operator(_other)); + auto term1 = product_operator(1., *this); + auto term2 = product_operator(1., other); + return operator_sum(term1, term2); } operator_sum elementary_operator::operator-(const elementary_operator &other) const { - std::vector> _this = { - *this}; - return operator_sum(product_operator(_this), (-1. * other)); + return operator_sum(product_operator(1., *this), -1. * other); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/instantiations.cpp index 52138d3ce3..77741ac1a9 100644 --- a/runtime/cudaq/dynamics/instantiations.cpp +++ b/runtime/cudaq/dynamics/instantiations.cpp @@ -22,11 +22,7 @@ namespace cudaq { template requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this scalar operator to the front of the terms list. - terms.insert(terms.begin(), other); - return product_operator(terms); + return product_operator(other * std::move(self.get_coefficient()), std::move(self.get_terms())); } template @@ -80,17 +76,15 @@ operator_sum operator-(const std::complex other, const produc template requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector> terms = - self.get_operators(); - /// Insert this elementary operator to the front of the terms list. + std::vector terms = std::move(self.get_terms()); terms.insert(terms.begin(), other); - return product_operator(terms); + return product_operator(std::move(self.get_coefficient()), std::move(terms)); } template requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(other), self); + return operator_sum(product_operator(1., other), self); } template @@ -114,7 +108,7 @@ template requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self) { std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), product_operator({other})); + terms.insert(terms.begin(), product_operator(other)); return operator_sum(terms); } @@ -123,7 +117,7 @@ requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self) { auto negative_self = self * (-1.); std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), product_operator({other})); + terms.insert(terms.begin(), product_operator(other)); return operator_sum(terms); } @@ -166,28 +160,19 @@ operator_sum operator-(std::complex other, const operator_sum template requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum * self; + return ((operator_sum)product_operator(1., other)) * self; } template requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum + self; + return ((operator_sum)product_operator(1., other)) + self; } template requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - std::vector> new_term = {other}; - std::vector> _prods = {product_operator(new_term)}; - auto selfOpSum = operator_sum(_prods); - return selfOpSum - self; + return ((operator_sum)product_operator(1., other)) - self; } // operator sum right-hand arithmetics @@ -273,7 +258,7 @@ operator_sum& operator_sum::operator-=(std::complex requires std::derived_from operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = this->get_terms(); + std::vector> combined_terms = std::move(this->get_terms()); for (auto &term : combined_terms) { term *= other; } @@ -283,9 +268,9 @@ operator_sum operator_sum::operator*(const scalar_operator template requires std::derived_from operator_sum operator_sum::operator+(const scalar_operator &other) const { - std::vector> combined_terms = this->get_terms(); - std::vector> _other = {other}; - combined_terms.push_back(product_operator(_other)); + // FIXME: reserve length + auto combined_terms = std::move(this->get_terms()); + combined_terms.push_back(product_operator(other)); return operator_sum(combined_terms); } @@ -329,18 +314,16 @@ operator_sum operator_sum::operator*(const HandlerTy &othe template requires std::derived_from operator_sum operator_sum::operator+(const HandlerTy &other) const { - std::vector> combined_terms = this->get_terms(); - std::vector> _other = { - other}; - combined_terms.push_back(product_operator(_other)); + auto combined_terms = std::move(this->get_terms()); + combined_terms.push_back(product_operator(1., other)); return operator_sum(combined_terms); } template requires std::derived_from operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = this->get_terms(); - combined_terms.push_back(other * (-1.)); + std::vector> combined_terms = std::move(this->get_terms()); + combined_terms.push_back(product_operator(-1., other)); return operator_sum(combined_terms); } @@ -354,16 +337,16 @@ operator_sum& operator_sum::operator*=(const HandlerTy &ot template requires std::derived_from operator_sum& operator_sum::operator+=(const HandlerTy &other) { - std::vector> _other = {other}; - *this = *this + product_operator(_other); + this->coefficients.push_back(1.); + this->terms.push_back({other}); return *this; } template requires std::derived_from operator_sum& operator_sum::operator-=(const HandlerTy &other) { - std::vector> _other = {other}; - *this = *this - product_operator(_other); + this->coefficients.push_back(-1.); + this->terms.push_back({other}); return *this; } @@ -431,9 +414,9 @@ operator_sum operator_sum::operator*(const operator_sum requires std::derived_from operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = this->get_terms(); - // make_move_iterator for && overload - combined_terms.insert(combined_terms.end(), other.terms.begin(), other.terms.end()); + std::vector> combined_terms = std::move(this->get_terms()); + std::vector> other_terms = std::move(other.get_terms()); + combined_terms.insert(combined_terms.end(), std::make_move_iterator(other_terms.begin()), std::make_move_iterator(other_terms.end())); return operator_sum(combined_terms); } @@ -520,25 +503,21 @@ product_operator& product_operator::operator*=(std::comple template requires std::derived_from product_operator product_operator::operator*(const scalar_operator &other) const { - std::vector> - combined_terms = operator_sum::terms[0]; - combined_terms.push_back(other); - return product_operator(combined_terms); + return product_operator(this->coefficients[0] * other, this->terms[0]); } template requires std::derived_from operator_sum product_operator::operator+(const scalar_operator &other) const { - std::vector> _other = { - other}; - return operator_sum(*this, product_operator(_other)); + product_operator coefficient(other); + return operator_sum(coefficient, *this); } template requires std::derived_from operator_sum product_operator::operator-(const scalar_operator &other) const { - std::vector> minus_term = {other * (-1.)}; - return operator_sum(*this, product_operator(minus_term)); + product_operator coefficient(-1. * other); + return operator_sum(coefficient, *this); } template @@ -551,24 +530,21 @@ product_operator& product_operator::operator*=(const scala template requires std::derived_from product_operator product_operator::operator*(const HandlerTy &other) const { - std::vector> - combined_terms = operator_sum::terms[0]; + auto combined_terms = this->terms[0]; combined_terms.push_back(other); - return product_operator(combined_terms); + return product_operator(1., combined_terms); } template requires std::derived_from operator_sum product_operator::operator+(const HandlerTy &other) const { - std::vector> _other = { - other}; - return operator_sum(*this, product_operator(_other)); + return operator_sum(*this, product_operator(1., other)); } template requires std::derived_from operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum(*this, other * (-1.)); + return operator_sum(*this, product_operator(-1., other)); } template @@ -581,10 +557,9 @@ product_operator& product_operator::operator*=(const Handl template requires std::derived_from product_operator product_operator::operator*(const product_operator &other) const { - std::vector> - combined_terms = operator_sum::terms[0]; + auto combined_terms = this->terms[0]; combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); - return product_operator(combined_terms); + return product_operator(1., combined_terms); } template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 39a9e5d03f..d8bb0dc5f1 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -146,18 +146,10 @@ matrix_2 product_operator::to_matrix( template std::vector product_operator::degrees() const { std::set unique_degrees; - // The variant type makes it difficult - auto beginFunc = [](auto &&t) { return t.degrees.begin(); }; - auto endFunc = [](auto &&t) { return t.degrees.end(); }; - for (const auto &term : operator_sum::terms[0]) { - unique_degrees.insert(std::visit(beginFunc, term), - std::visit(endFunc, term)); - } - // Erase any `-1` degree values that may have come from scalar operators. - auto it = unique_degrees.find(-1); - if (it != unique_degrees.end()) { - unique_degrees.erase(it); + for (const HandlerTy &term : this->get_terms()) { + unique_degrees.insert(term.degrees.begin(), term.degrees.end()); } + // FIXME: SORT THE DEGREES return std::vector(unique_degrees.begin(), unique_degrees.end()); } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index c03e1f377b..2463910cbd 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -66,7 +66,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); #define ARITHMETIC_OPERATIONS_DOUBLES(op) \ scalar_operator scalar_operator::operator op(double other) const { \ auto newGenerator = \ - [=, this](std::map> parameters) { \ + [=, *this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(newGenerator); \ @@ -103,7 +103,7 @@ ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); scalar_operator scalar_operator::operator op( \ std::complex other) const{ \ auto newGenerator = \ - [=, this](std::map> parameters) { \ + [=, *this](std::map> parameters) { \ return this->evaluate(parameters) op other; \ }; \ return scalar_operator(newGenerator); \ @@ -141,7 +141,7 @@ ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); scalar_operator scalar_operator::operator op( \ const scalar_operator &other) const { \ auto newGenerator = \ - [=, this](std::map> parameters) { \ + [=, *this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(newGenerator); \ diff --git a/runtime/cudaq/dynamics/template_declarations.h b/runtime/cudaq/dynamics/template_declarations.h new file mode 100644 index 0000000000..e1cec53736 --- /dev/null +++ b/runtime/cudaq/dynamics/template_declarations.h @@ -0,0 +1,154 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +//#include +#include +#include + +namespace cudaq { + +class scalar_operator; + +class elementary_operator; + +template +requires std::derived_from +class product_operator; + +template +requires std::derived_from +class operator_sum; + +template +requires std::derived_from +product_operator operator*(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(double other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(double other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +requires std::derived_from +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const product_operator &self); + +template +requires std::derived_from +operator_sum operator*(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(double other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(std::complex other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator*(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator+(const HandlerTy &other, const operator_sum &self); +template +requires std::derived_from +operator_sum operator-(const HandlerTy &other, const operator_sum &self); + + +#ifndef CUDAQ_INSTANTIATE_TEMPLATES +extern template +product_operator operator*(double other, const product_operator &self); +extern template +operator_sum operator+(double other, const product_operator &self); +extern template +operator_sum operator-(double other, const product_operator &self); +extern template +product_operator operator*(std::complex other, const product_operator &self); +extern template +operator_sum operator+(std::complex other, const product_operator &self); +extern template +operator_sum operator-(std::complex other, const product_operator &self); +extern template +product_operator operator*(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +extern template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +extern template +product_operator operator*(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +extern template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + +extern template +operator_sum operator*(double other, const operator_sum &self); +extern template +operator_sum operator+(double other, const operator_sum &self); +extern template +operator_sum operator-(double other, const operator_sum &self); +extern template +operator_sum operator*(std::complex other, const operator_sum &self); +extern template +operator_sum operator+(std::complex other, const operator_sum &self); +extern template +operator_sum operator-(std::complex other, const operator_sum &self); +extern template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +extern template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +extern template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); +#endif + +} \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 7e98629fd4..7702242b6c 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,6 +8,7 @@ #pragma once +#include "dynamics/template_declarations.h" #include "definition.h" #include "utils/tensor.h" @@ -20,145 +21,130 @@ namespace cudaq { -class scalar_operator; +class scalar_operator { -class elementary_operator; +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::optional> m_constant_value; -template -requires std::derived_from -class product_operator; + /// @brief The function that generates the value of the scalar operator. + /// The function can take a vector of complex-valued arguments + /// and returns a number. + ScalarCallbackFunction generator; -template -requires std::derived_from -class operator_sum; +public: + scalar_operator(double value) + : m_constant_value(value), generator() {} -template -requires std::derived_from -product_operator operator*(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(double other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(double other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -requires std::derived_from -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const product_operator &self); + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value) + : m_constant_value(value), generator() {} -template -requires std::derived_from -operator_sum operator*(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(double other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(std::complex other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -requires std::derived_from -operator_sum operator-(const HandlerTy &other, const operator_sum &self); + scalar_operator(const ScalarCallbackFunction &create) + : m_constant_value(), generator(create) {} -#ifdef CUDAQ_INSTANTIATE_TEMPLATES -#else -extern template -product_operator operator*(double other, const product_operator &self); -extern template -operator_sum operator+(double other, const product_operator &self); -extern template -operator_sum operator-(double other, const product_operator &self); -extern template -product_operator operator*(std::complex other, const product_operator &self); -extern template -operator_sum operator+(std::complex other, const product_operator &self); -extern template -operator_sum operator-(std::complex other, const product_operator &self); -extern template -product_operator operator*(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -extern template -product_operator operator*(const elementary_operator &other, const product_operator &self); -extern template -operator_sum operator+(const elementary_operator &other, const product_operator &self); -extern template -operator_sum operator-(const elementary_operator &other, const product_operator &self); - -extern template -operator_sum operator*(double other, const operator_sum &self); -extern template -operator_sum operator+(double other, const operator_sum &self); -extern template -operator_sum operator-(double other, const operator_sum &self); -extern template -operator_sum operator*(std::complex other, const operator_sum &self); -extern template -operator_sum operator+(std::complex other, const operator_sum &self); -extern template -operator_sum operator-(std::complex other, const operator_sum &self); -extern template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); -#endif + /// @brief Constructor that just takes a callback function with no + /// arguments. + scalar_operator(ScalarCallbackFunction &&create) + : m_constant_value() { + generator = std::move(create); + } + + // copy constructor + scalar_operator(const scalar_operator &other) + : m_constant_value(other.m_constant_value), generator(other.generator) {} + + // move constructor + scalar_operator(scalar_operator &&other) + : m_constant_value(other.m_constant_value) { + generator = std::move(other.generator); + } + + // assignment operator + scalar_operator& operator=(const scalar_operator &other) { + if (this != &other) { + m_constant_value = other.m_constant_value; + generator = other.generator; + } + return *this; + } + + // move assignment operator + scalar_operator& operator=(scalar_operator &&other) { + if (this != &other) { + m_constant_value = other.m_constant_value; + generator = std::move(other.generator); + } + return *this; + } + + /// NOTE: We should revisit these constructors and remove any that have + /// become unnecessary as the implementation improves. + // scalar_operator() = default; + // Copy constructor. + // scalar_operator(const scalar_operator &other); + // scalar_operator(scalar_operator &other); + + ~scalar_operator() = default; + + // Need this property for consistency with other inherited types. + // Particularly, to be used when the scalar operator is held within + // a variant type next to elementary operators. + std::vector degrees = {}; + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(const std::map> parameters) const; + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatibility with the other inherited classes. + matrix_2 to_matrix(const std::map dimensions, + const std::map> parameters) const; + + // Arithmetic overloads against other operator types. + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + scalar_operator& operator*=(double other); + scalar_operator& operator/=(double other); + scalar_operator& operator+=(double other); + scalar_operator& operator-=(double other); + scalar_operator operator*(std::complex other) const; + scalar_operator operator/(std::complex other) const; + scalar_operator operator+(std::complex other) const; + scalar_operator operator-(std::complex other) const; + scalar_operator& operator*=(std::complex other); + scalar_operator& operator/=(std::complex other); + scalar_operator& operator+=(std::complex other); + scalar_operator& operator-=(std::complex other); + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; + scalar_operator& operator*=(const scalar_operator &other); + scalar_operator& operator/=(const scalar_operator &other); + scalar_operator& operator+=(const scalar_operator &other); + scalar_operator& operator-=(const scalar_operator &other); + /// TODO: implement and test pow + + friend scalar_operator operator*(double other, const scalar_operator &self); + friend scalar_operator operator/(double other, const scalar_operator &self); + friend scalar_operator operator+(double other, const scalar_operator &self); + friend scalar_operator operator-(double other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, const scalar_operator &self); + friend scalar_operator operator/(std::complex other, const scalar_operator &self); + friend scalar_operator operator+(std::complex other, const scalar_operator &self); + friend scalar_operator operator-(std::complex other, const scalar_operator &self); + + // /// @brief Returns true if other is a scalar operator with the same + // /// generator. + // bool operator==(scalar_operator other); +}; /// @brief Represents an operator expression consisting of a sum of terms, where @@ -177,18 +163,22 @@ class operator_sum { _canonical_terms() const; void aggregate_terms(const product_operator& head) { - terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + terms.push_back(head.terms[0]); + coefficients.push_back(head.coefficients[0]); } - + template void aggregate_terms(const product_operator &head, Args&& ... args) { - terms.push_back(head.terms[0]); // ops is a vector of vectors where the outermost vector has only one entry + terms.push_back(head.terms[0]); + coefficients.push_back(head.coefficients[0]); aggregate_terms(std::forward(args)...); } protected: + operator_sum() = default; - std::vector>> terms; + std::vector> terms; + std::vector coefficients; public: @@ -198,15 +188,17 @@ class operator_sum { /// is a product of elementary and scalar operators. template, Args>...>::value, void>> operator_sum(const Args&... args) { - std::cout << "op sum constructor" << std::endl; terms.reserve(sizeof...(Args)); + coefficients.reserve(sizeof...(Args)); aggregate_terms(args...); } operator_sum(const std::vector>& terms) { this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); for (const product_operator& term : terms) { this->terms.push_back(term.terms[0]); + this->coefficients.push_back(term.coefficients[0]); } } @@ -214,20 +206,22 @@ class operator_sum { this->terms.reserve(terms.size()); for (const product_operator& term : terms) { this->terms.push_back(std::move(term.terms[0])); + this->coefficients.push_back(std::move(term.coefficients[0])); } } // copy constructor operator_sum(const operator_sum &other) - : terms(other.terms) {} + : coefficients(other.coefficients), terms(other.terms) {} // move constructor operator_sum(operator_sum &&other) - : terms(std::move(other.terms)) {} + : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} // assignment operator operator_sum& operator=(const operator_sum& other) { if (this != &other) { + coefficients = other.coefficients; terms = other.terms; } return *this; @@ -235,7 +229,10 @@ class operator_sum { // move assignment operator operator_sum& operator=(operator_sum &&other) { - terms = std::move(other.terms); + if (this != &other) { + coefficients = std::move(other.coefficients); + terms = std::move(other.terms); + } return *this; } @@ -353,8 +350,8 @@ class operator_sum { std::vector> get_terms() const { std::vector> prods; prods.reserve(terms.size()); - for (auto term : terms) { - prods.push_back(product_operator(term)); + for (size_t i = 0; i < terms.size(); ++i) { + prods.push_back(product_operator(coefficients[i], terms[i])); } return prods; } }; @@ -380,50 +377,62 @@ class product_operator : public operator_sum { } public: + + product_operator(scalar_operator coefficient) { + operator_sum::terms.push_back({}); + operator_sum::coefficients.push_back(std::move(coefficient)); + } + // Constructor for an operator expression that represents a product // of scalar and elementary operators. // arg atomic_operators : The operators of which to compute the product when // evaluating the operator expression. template...>::value, void>> - product_operator(const Args&... args) { - std::cout << "prod op constructor" << std::endl; - std::vector> ops = {}; + product_operator(scalar_operator coefficient, const Args&... args) { + operator_sum::coefficients.push_back(std::move(coefficient)); + std::vector ops = {}; ops.reserve(sizeof...(Args)); operator_sum::terms.push_back(ops); aggregate_terms(args...); } - product_operator(const std::vector>& atomic_operators) { - std::cout << "prod op constructor" << std::endl; + product_operator(scalar_operator coefficient, const std::vector& atomic_operators) { operator_sum::terms.push_back(atomic_operators); + operator_sum::coefficients.push_back(std::move(coefficient)); } - product_operator(std::vector>&& atomic_operators) { - std::cout << "prod op constructor" << std::endl; + product_operator(scalar_operator coefficient, std::vector&& atomic_operators) { operator_sum::terms.push_back(std::move(atomic_operators)); + operator_sum::coefficients.push_back(std::move(coefficient)); } // copy constructor product_operator(const product_operator &other) { operator_sum::terms = other.terms; + operator_sum::coefficients = other.coefficients; } // move constructor product_operator(product_operator &&other) { operator_sum::terms = std::move(other.terms); + operator_sum::coefficients = std::move(other.coefficients); } // assignment operator product_operator& operator=(const product_operator& other) { if (this != &other) { operator_sum::terms = other.terms; + operator_sum::coefficients = other.coefficients; } return *this; } // move assignment operator product_operator& operator=(product_operator &&other) { - operator_sum::terms = std::move(other.terms); + if (this != &other) { + this->coefficients = std::move(other.coefficients); + this->terms = std::move(other.terms); + } return *this; } @@ -437,11 +446,6 @@ class product_operator : public operator_sum { /// operator. int term_count() const { return operator_sum::terms[0].size(); } - /// FIXME: ELIMINATE THIS - std::vector> get_operators() const { - return operator_sum::terms[0]; - }; - /// @brief Return the `product_operator` as a string. std::string to_string() const; @@ -520,131 +524,14 @@ class product_operator : public operator_sum { /// If the equality evaluates to True, on the other hand, the operators /// are guaranteed to represent the same transformation for all arguments. bool operator==(product_operator other); -}; - -// FIXME: check if we really need the inheritance from prod operator, and if so what it should be -// (check if this really should be its own class?) -// -> replace elementary operator with the operator handler, the current elem op is the handler for custom op -// -> scalar remains its own op class, but likely doesn't need to be convertible to operator (i.e. no inheritance) -class scalar_operator { - -private: - // If someone gave us a constant value, we will just return that - // directly to them when they call `evaluate`. - std::optional> m_constant_value; - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; - -public: - scalar_operator(double value) - : m_constant_value(value), generator() {} - - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value) - : m_constant_value(value), generator() {} - - - scalar_operator(const ScalarCallbackFunction &create) - : m_constant_value(), generator(create) {} - - /// @brief Constructor that just takes a callback function with no - /// arguments. - scalar_operator(ScalarCallbackFunction &&create) - : m_constant_value() { - generator = std::move(create); - } - - // copy constructor - scalar_operator(const scalar_operator &other) - : m_constant_value(other.m_constant_value), generator(other.generator) {} - - // move constructor - scalar_operator(scalar_operator &&other) - : m_constant_value(other.m_constant_value) { - generator = std::move(other.generator); - } - - // assignment operator - scalar_operator& operator=(const scalar_operator &other) { - m_constant_value = other.m_constant_value; - generator = other.generator; - return *this; - } - - // move assignment operator - scalar_operator& operator=(scalar_operator &&other) { - m_constant_value = other.m_constant_value; - generator = std::move(other.generator); - return *this; - } - - /// NOTE: We should revisit these constructors and remove any that have - /// become unnecessary as the implementation improves. - // scalar_operator() = default; - // Copy constructor. - // scalar_operator(const scalar_operator &other); - // scalar_operator(scalar_operator &other); - - ~scalar_operator() = default; - - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {}; - - /// @brief Return the scalar operator as a concrete complex value. - std::complex - evaluate(const std::map> parameters) const; - - // Return the scalar operator as a 1x1 matrix. This is needed for - // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions, - const std::map> parameters) const; - - // Arithmetic overloads against other operator types. - scalar_operator operator*(double other) const; - scalar_operator operator/(double other) const; - scalar_operator operator+(double other) const; - scalar_operator operator-(double other) const; - scalar_operator& operator*=(double other); - scalar_operator& operator/=(double other); - scalar_operator& operator+=(double other); - scalar_operator& operator-=(double other); - scalar_operator operator*(std::complex other) const; - scalar_operator operator/(std::complex other) const; - scalar_operator operator+(std::complex other) const; - scalar_operator operator-(std::complex other) const; - scalar_operator& operator*=(std::complex other); - scalar_operator& operator/=(std::complex other); - scalar_operator& operator+=(std::complex other); - scalar_operator& operator-=(std::complex other); - scalar_operator operator*(const scalar_operator &other) const; - scalar_operator operator/(const scalar_operator &other) const; - scalar_operator operator+(const scalar_operator &other) const; - scalar_operator operator-(const scalar_operator &other) const; - scalar_operator& operator*=(const scalar_operator &other); - scalar_operator& operator/=(const scalar_operator &other); - scalar_operator& operator+=(const scalar_operator &other); - scalar_operator& operator-=(const scalar_operator &other); - /// TODO: implement and test pow - friend scalar_operator operator*(double other, const scalar_operator &self); - friend scalar_operator operator/(double other, const scalar_operator &self); - friend scalar_operator operator+(double other, const scalar_operator &self); - friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, const scalar_operator &self); - friend scalar_operator operator/(std::complex other, const scalar_operator &self); - friend scalar_operator operator+(std::complex other, const scalar_operator &self); - friend scalar_operator operator-(std::complex other, const scalar_operator &self); + /// FIXME: Protect this once I can do deeper testing in `unittests`. + // protected: + std::vector get_terms() const { + return operator_sum::terms[0]; } - // /// @brief Returns true if other is a scalar operator with the same - // /// generator. - // bool operator==(scalar_operator other); + scalar_operator get_coefficient() const { + return operator_sum::coefficients[0]; } }; @@ -669,15 +556,11 @@ class elementary_operator { /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. elementary_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), degrees(degrees) { - std::cout << "elem op constructor" << std::endl; - } + : id(operator_id), degrees(degrees) {} // constructor elementary_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), degrees(std::move(degrees)) { - std::cout << "elem op constructor" << std::endl; - } + : id(operator_id), degrees(std::move(degrees)) {} // copy constructor elementary_operator(const elementary_operator &other) @@ -748,7 +631,9 @@ class elementary_operator { /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(elementary_operator other); + bool operator==(const elementary_operator &other) const { + return this->id == other.id && this->degrees == other.degrees; + } // Predefined operators. static elementary_operator identity(int degree); diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 0127e0bc39..728cb9b7a9 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -103,6 +103,15 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // return mat.exp(); // } +void assert_product_equal(const cudaq::product_operator &got, + const cudaq::product_operator &expected) { + + auto sumterms_prod = ((cudaq::operator_sum)got).get_terms(); + ASSERT_TRUE(sumterms_prod.size() == 1); + ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected.get_coefficient().evaluate({})); + ASSERT_TRUE(got.get_terms() == expected.get_terms()); +} + } // namespace utils_0 TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { @@ -236,8 +245,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + auto expected = cudaq::product_operator(const_scale_factor, self); + utils_0::assert_product_equal(product, expected); + utils_0::assert_product_equal(reverse, expected); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -263,8 +273,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + auto expected = cudaq::product_operator(other, self); + utils_0::assert_product_equal(product, expected); + utils_0::assert_product_equal(reverse, expected); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index ac8fc63887..57e8ead2ad 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -234,18 +234,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto product = sum * cudaq::scalar_operator(1.0); - auto reverse = cudaq::scalar_operator(1.0) * sum; + auto product = sum * cudaq::scalar_operator(0.1); + auto reverse = cudaq::scalar_operator(0.1) * sum; ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); } } @@ -278,11 +280,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - sum *= cudaq::scalar_operator(1.0); + sum *= cudaq::scalar_operator(0.1); ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); } } @@ -322,11 +325,13 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(reverse.term_count() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); } } @@ -363,7 +368,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + std::cout << "GOT: " << term.get_coefficient().evaluate({}) << std::endl; + ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); } } @@ -400,11 +407,13 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(reverse.term_count() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); } } @@ -443,7 +452,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index cf4660af80..64367c6779 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -350,11 +350,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); + ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + auto product = value_0 * product_op; auto reverse = product_op * value_0; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); + ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -366,11 +371,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); + ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + auto product = 2.0 * product_op; auto reverse = product_op * 2.0; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -381,13 +391,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); + ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + + auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); + ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == scalar_op.evaluate({})); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -400,7 +415,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= value_0; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -412,7 +428,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= 2.0; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -422,11 +439,12 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { { auto product = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); - + auto scalar_op = cudaq::scalar_operator(0.1); product *= scalar_op; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); + ASSERT_TRUE(scalar_op.evaluate({}) == std::complex(0.1)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); From 69a341226e3de3b3db6055ef0298def89ffdc049 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 27 Jan 2025 21:40:43 +0000 Subject: [PATCH 178/311] getting rid of all elementary operator overloads Signed-off-by: Bettina Heim --- .../cudaq/dynamics/elementary_operators.cpp | 188 ++++-------------- runtime/cudaq/dynamics/product_operators.cpp | 12 ++ runtime/cudaq/operators.h | 54 ++--- .../dynamics/elementary_ops_arithmetic.cpp | 17 +- unittests/dynamics/elementary_ops_simple.cpp | 1 - 5 files changed, 73 insertions(+), 199 deletions(-) diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index b9b32e4682..8e5f42b7e3 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -17,7 +17,7 @@ namespace cudaq { std::map elementary_operator::m_ops = {}; -elementary_operator elementary_operator::identity(int degree) { +product_operator elementary_operator::identity(int degree) { std::string op_id = "identity"; auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. @@ -36,41 +36,37 @@ elementary_operator elementary_operator::identity(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::zero(int degree) { +product_operator elementary_operator::zero(int degree) { std::string op_id = "zero"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when // the user invokes this later on via, e.g, `to_matrix()`. - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); return mat; }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::annihilate(int degree) { +product_operator elementary_operator::annihilate(int degree) { std::string op_id = "annihilate"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -80,19 +76,17 @@ elementary_operator elementary_operator::annihilate(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::create(int degree) { +product_operator elementary_operator::create(int degree) { std::string op_id = "create"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -102,19 +96,17 @@ elementary_operator elementary_operator::create(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::position(int degree) { +product_operator elementary_operator::position(int degree) { std::string op_id = "position"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) @@ -128,19 +120,17 @@ elementary_operator elementary_operator::position(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::momentum(int degree) { +product_operator elementary_operator::momentum(int degree) { std::string op_id = "momentum"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) @@ -154,19 +144,17 @@ elementary_operator elementary_operator::momentum(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::number(int degree) { +product_operator elementary_operator::number(int degree) { std::string op_id = "number"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -176,19 +164,17 @@ elementary_operator elementary_operator::number(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator elementary_operator::parity(int degree) { +product_operator elementary_operator::parity(int degree) { std::string op_id = "parity"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&](std::map dimensions, + auto func = [&, degree](std::map dimensions, std::map> _none) { - auto degree = op.degrees[0]; std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -198,20 +184,18 @@ elementary_operator elementary_operator::parity(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return op; + return product_operator(1., op); } -elementary_operator +product_operator elementary_operator::displace(int degree, std::complex amplitude) { std::string op_id = "displace"; - std::vector degrees = {degree}; - auto op = elementary_operator(op_id, degrees); + auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; // if (op.m_ops.find(op_id) == op.m_ops.end()) { - // auto func = [&](std::map dimensions, + // auto func = [&, degree](std::map dimensions, // std::map> _none) { - // auto degree = op.degrees[0]; // std::size_t dimension = dimensions[degree]; // auto temp_mat = matrix_2(dimension, dimension); // // // displace = exp[ (amplitude * create) - (conj(amplitude) * @@ -233,10 +217,10 @@ elementary_operator::displace(int degree, std::complex amplitude) { // op.define(op_id, op.expected_dimensions, func); // } throw std::runtime_error("currently have a bug in implementation."); - return op; + return product_operator(1., op); } -elementary_operator +product_operator elementary_operator::squeeze(int degree, std::complex amplitude) { throw std::runtime_error("Not yet implemented."); } @@ -249,106 +233,4 @@ matrix_2 elementary_operator::to_matrix( return m_ops[id].generator(dimensions, parameters); } -// left-hand arithmetics - -product_operator operator*(double other, const elementary_operator &self) { - return product_operator(other, self); -} - -operator_sum operator+(double other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., self)); -} - -operator_sum operator-(double other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, -1. * self); -} - -product_operator operator*(std::complex other, const elementary_operator &self) { - return product_operator(other, self); -} - -operator_sum operator+(std::complex other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., self)); -} - -operator_sum operator-(std::complex other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, -1. * self); -} - -product_operator operator*(const scalar_operator &other, const elementary_operator &self) { - return product_operator(other, self); -} - -operator_sum operator+(const scalar_operator &other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., self)); -} - -operator_sum operator-(const scalar_operator &other, const elementary_operator &self) { - product_operator coefficient(other); - return operator_sum(coefficient, -1. * self); -} - -// right-hand arithmetics - -product_operator elementary_operator::operator*(double other) const { - return product_operator(other, *this); -} - -operator_sum elementary_operator::operator+(double other) const { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -operator_sum elementary_operator::operator-(double other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -product_operator elementary_operator::operator*(std::complex other) const { - return product_operator(other, *this); -} - -operator_sum elementary_operator::operator+(std::complex other) const { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -operator_sum elementary_operator::operator-(std::complex other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -product_operator elementary_operator::operator*(const scalar_operator &other) const { - return product_operator(other, *this); -} - -operator_sum elementary_operator::operator+(const scalar_operator &other) const { - product_operator coefficient(other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -operator_sum elementary_operator::operator-(const scalar_operator &other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, product_operator(1., *this)); -} - -product_operator elementary_operator::operator*(const elementary_operator &other) const { - return product_operator(1., *this, other); -} - -operator_sum elementary_operator::operator+(const elementary_operator &other) const { - auto term1 = product_operator(1., *this); - auto term2 = product_operator(1., other); - return operator_sum(term1, term2); -} - -operator_sum elementary_operator::operator-(const elementary_operator &other) const { - return operator_sum(product_operator(1., *this), -1. * other); -} - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index d8bb0dc5f1..356fe4b25b 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -142,6 +142,18 @@ matrix_2 product_operator::to_matrix( // return out; // } + +// FIXME: remove - to be replaced with the general implementation for product op +template<> +matrix_2 product_operator::to_matrix( + std::map dimensions, + std::map> parameters) { + if (this->get_coefficient() != scalar_operator(1.) || this->term_count() != 1) + throw std::runtime_error("not implemented"); + return this->get_terms()[0].to_matrix(dimensions, parameters); +} + + // Degrees property template std::vector product_operator::degrees() const { diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 7702242b6c..a2cc48a570 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -143,7 +143,13 @@ class scalar_operator { // /// @brief Returns true if other is a scalar operator with the same // /// generator. - // bool operator==(scalar_operator other); + bool operator==(scalar_operator other) { + if (this->m_constant_value.has_value() && other.m_constant_value.has_value()) { + return this->m_constant_value == other.m_constant_value; + } else { + throw std::runtime_error("not implemented"); + } + } }; @@ -605,30 +611,6 @@ class elementary_operator { to_matrix(const std::map dimensions, const std::map> parameters) const; - // Arithmetic overloads - product_operator operator*(double other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - product_operator operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - product_operator operator*(const elementary_operator &other) const; - operator_sum operator+(const elementary_operator &other) const; - operator_sum operator-(const elementary_operator &other) const; - - friend product_operator operator*(double other, const elementary_operator &self); - friend operator_sum operator+(double other, const elementary_operator &self); - friend operator_sum operator-(double other, const elementary_operator &self); - friend product_operator operator*(std::complex other, const elementary_operator &self); - friend operator_sum operator+(std::complex other, const elementary_operator &self); - friend operator_sum operator-(std::complex other, const elementary_operator &self); - friend product_operator operator*(const scalar_operator &other, const elementary_operator &self); - friend operator_sum operator+(const scalar_operator &other, const elementary_operator &self); - friend operator_sum operator-(const scalar_operator &other, const elementary_operator &self); - /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. bool operator==(const elementary_operator &other) const { @@ -636,18 +618,18 @@ class elementary_operator { } // Predefined operators. - static elementary_operator identity(int degree); - static elementary_operator zero(int degree); - static elementary_operator annihilate(int degree); - static elementary_operator create(int degree); - static elementary_operator momentum(int degree); - static elementary_operator number(int degree); - static elementary_operator parity(int degree); - static elementary_operator position(int degree); - /// FIXME: - static elementary_operator squeeze(int degree, + static product_operator identity(int degree); + static product_operator zero(int degree); + static product_operator annihilate(int degree); + static product_operator create(int degree); + static product_operator momentum(int degree); + static product_operator number(int degree); + static product_operator parity(int degree); + static product_operator position(int degree); + /// FIXME: amplitude should be a parameter that is only defined upon evaluation + static product_operator squeeze(int degree, std::complex amplitude); - static elementary_operator displace(int degree, + static product_operator displace(int degree, std::complex amplitude); /// @brief Adds the definition of an elementary operator with the given id to diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 728cb9b7a9..a88d3c7b38 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -104,12 +104,13 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } void assert_product_equal(const cudaq::product_operator &got, - const cudaq::product_operator &expected) { + const std::complex &expected_coefficient, + const std::vector &expected_terms) { auto sumterms_prod = ((cudaq::operator_sum)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); - ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected.get_coefficient().evaluate({})); - ASSERT_TRUE(got.get_terms() == expected.get_terms()); + ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected_coefficient); + ASSERT_TRUE(got.get_terms() == expected_terms); } } // namespace utils_0 @@ -245,9 +246,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - auto expected = cudaq::product_operator(const_scale_factor, self); - utils_0::assert_product_equal(product, expected); - utils_0::assert_product_equal(reverse, expected); + utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -273,9 +273,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - auto expected = cudaq::product_operator(other, self); - utils_0::assert_product_equal(product, expected); - utils_0::assert_product_equal(reverse, expected); + utils_0::assert_product_equal(product, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp index b45d3d8952..3b12805d3a 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -108,7 +108,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Identity operator. { for (auto level_count : levels) { - // cudaq::operators::identity(int degree) auto id = cudaq::elementary_operator::identity(degree_index); auto got_id = id.to_matrix({{degree_index, level_count}}, {}); auto want_id = utils::id_matrix(level_count); From ced3e6a42f789c863bebe20faae2c93475267f63 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 28 Jan 2025 13:37:02 +0000 Subject: [PATCH 179/311] clean up for scalars Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/scalar_operators.cpp | 255 ++++++++++-------- runtime/cudaq/operators.h | 92 +++---- .../dynamics/elementary_ops_arithmetic.cpp | 6 +- unittests/dynamics/operator_sum.cpp | 20 +- .../dynamics/product_operators_arithmetic.cpp | 26 +- unittests/dynamics/scalar_ops_arithmetic.cpp | 58 ++-- unittests/dynamics/scalar_ops_simple.cpp | 16 +- 7 files changed, 243 insertions(+), 230 deletions(-) diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 2463910cbd..f7f43536e7 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,11 +14,63 @@ namespace cudaq { +// constructors and destructors + +scalar_operator::scalar_operator(double value) + : constant_value(value), generator() {} + +scalar_operator::scalar_operator(std::complex value) + : constant_value(value), generator() {} + +scalar_operator::scalar_operator(const ScalarCallbackFunction &create) + : constant_value(), generator(create) {} + +scalar_operator::scalar_operator(ScalarCallbackFunction &&create) + : constant_value() { + generator = std::move(create); +} + +scalar_operator::scalar_operator(const scalar_operator &other) + : constant_value(other.constant_value), generator(other.generator) {} + +scalar_operator::scalar_operator(scalar_operator &&other) + : constant_value(other.constant_value) { + generator = std::move(other.generator); +} + +// assignments + +scalar_operator& scalar_operator::operator=(const scalar_operator &other) { + if (this != &other) { + constant_value = other.constant_value; + generator = other.generator; + } + return *this; +} + +scalar_operator& scalar_operator::operator=(scalar_operator &&other) { + if (this != &other) { + constant_value = other.constant_value; + generator = std::move(other.generator); + } + return *this; +} + +// comparison + +bool scalar_operator::operator==(scalar_operator other) { + if (this->constant_value.has_value() && other.constant_value.has_value()) { + return this->constant_value == other.constant_value; + } else { + throw std::runtime_error("not implemented"); + } +} + // evaluations std::complex scalar_operator::evaluate( const std::map> parameters) const { - if (m_constant_value.has_value()) return m_constant_value.value(); + if (constant_value.has_value()) return constant_value.value(); else return generator(parameters); } @@ -30,118 +82,41 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } -// left-hand arithmetics - -#define ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(op) \ - scalar_operator operator op(double other, const scalar_operator &self) { \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ - } - -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_DOUBLES_REVERSE(-); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(op) \ - scalar_operator operator op(std::complex other, \ - const scalar_operator &self) { \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return other op self.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_REVERSE(-); - // right-hand arithmetics -#define ARITHMETIC_OPERATIONS_DOUBLES(op) \ - scalar_operator scalar_operator::operator op(double other) const { \ - auto newGenerator = \ - [=, *this](std::map> parameters) { \ - return this->evaluate(parameters) op other; \ - }; \ - return scalar_operator(newGenerator); \ - } - -ARITHMETIC_OPERATIONS_DOUBLES(*); -ARITHMETIC_OPERATIONS_DOUBLES(/); -ARITHMETIC_OPERATIONS_DOUBLES(+); -ARITHMETIC_OPERATIONS_DOUBLES(-); - -#define ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op(double other) { \ - if (this->m_constant_value.has_value()) { \ - this->m_constant_value.value() op other; \ - return *this; \ +#define ARITHMETIC_OPERATIONS(op, otherTy) \ + scalar_operator scalar_operator::operator op(otherTy other) const { \ + if (this->constant_value.has_value()) { \ + return scalar_operator(this->constant_value.value() op other); \ } \ - /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in-place. */ \ - scalar_operator prevSelf(*this); \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return prevSelf.evaluate(parameters) op other; \ - }; \ - this->generator = newGenerator; \ - return *this; \ - } - -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_DOUBLES_ASSIGNMENT(-=); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(op) \ - scalar_operator scalar_operator::operator op( \ - std::complex other) const{ \ auto newGenerator = \ - [=, *this](std::map> parameters) { \ - return this->evaluate(parameters) op other; \ - }; \ + [other, generator = this->generator]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ return scalar_operator(newGenerator); \ } -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(*); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(/); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(+); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES(-); - -#define ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op( \ - std::complex other) { \ - if (this->m_constant_value.has_value()) { \ - this->m_constant_value.value() op other; \ - return *this; \ - } \ - /* Need to move the existing generating function to a new operator so that \ - * we can modify the generator in-place. */ \ - scalar_operator prevSelf(*this); \ - auto newGenerator = \ - [=](std::map> parameters) { \ - return prevSelf.evaluate(parameters) op other; \ - }; \ - this->generator = newGenerator; \ - return *this; \ - } - -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_COMPLEX_DOUBLES_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS(*, double); +ARITHMETIC_OPERATIONS(/, double); +ARITHMETIC_OPERATIONS(+, double); +ARITHMETIC_OPERATIONS(-, double); +ARITHMETIC_OPERATIONS(*, std::complex); +ARITHMETIC_OPERATIONS(/, std::complex); +ARITHMETIC_OPERATIONS(+, std::complex); +ARITHMETIC_OPERATIONS(-, std::complex); #define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ scalar_operator scalar_operator::operator op( \ const scalar_operator &other) const { \ + if (this->constant_value.has_value() && \ + other.constant_value.has_value()) { \ + auto res = this->constant_value.value() op other.constant_value.value(); \ + return scalar_operator(res); \ + } \ auto newGenerator = \ - [=, *this](std::map> parameters) { \ + [other, *this]( \ + std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(newGenerator); \ @@ -152,20 +127,42 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(/); ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); +#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ + scalar_operator& scalar_operator::operator op(otherTy other) { \ + if (this->constant_value.has_value()) { \ + this->constant_value.value() op other; \ + return *this; \ + } \ + auto newGenerator = \ + [other, generator = std::move(this->generator)]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ + this->generator = newGenerator; \ + return *this; \ + } + +ARITHMETIC_OPERATIONS_ASSIGNMENT(*=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(/=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(*=, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(/=, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, std::complex); + #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ scalar_operator& scalar_operator::operator op( \ const scalar_operator &other) { \ - if (this->m_constant_value.has_value() && \ - other.m_constant_value.has_value()) { \ - this->m_constant_value.value() op other.m_constant_value.value(); \ - return *this; \ + if (this->constant_value.has_value() && \ + other.constant_value.has_value()) { \ + this->constant_value.value() op other.constant_value.value(); \ + return *this; \ } \ - /* Need to move the existing generating function to a new operator so \ - * that we can modify the generator in-place. */ \ - scalar_operator prevSelf(*this); \ auto newGenerator = \ - [=](std::map> parameters) { \ - return prevSelf.evaluate(parameters) op other.evaluate(parameters); \ + [other, *this]( \ + std::map> parameters) { \ + return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ this->generator = newGenerator; \ return *this; \ @@ -176,4 +173,42 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ + scalar_operator operator op(scalar_operator &&self, otherTy other) { \ + return std::move(self op##= other); \ + } + +ARITHMETIC_OPERATIONS_RVALUE(*, double); +ARITHMETIC_OPERATIONS_RVALUE(/, double); +ARITHMETIC_OPERATIONS_RVALUE(+, double); +ARITHMETIC_OPERATIONS_RVALUE(-, double); +ARITHMETIC_OPERATIONS_RVALUE(*, std::complex); +ARITHMETIC_OPERATIONS_RVALUE(/, std::complex); +ARITHMETIC_OPERATIONS_RVALUE(+, std::complex); +ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); + +// left-hand arithmetics + +#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ + scalar_operator operator op(otherTy other, const scalar_operator &self) { \ + if (self.constant_value.has_value()) { \ + return scalar_operator(other op self.constant_value.value()); \ + } \ + auto newGenerator = \ + [other, generator = self.generator]( \ + std::map> parameters) { \ + return other op generator(parameters); \ + }; \ + return scalar_operator(newGenerator); \ + } + +ARITHMETIC_OPERATIONS_REVERSE(*, double); +ARITHMETIC_OPERATIONS_REVERSE(/, double); +ARITHMETIC_OPERATIONS_REVERSE(+, double); +ARITHMETIC_OPERATIONS_REVERSE(-, double); +ARITHMETIC_OPERATIONS_REVERSE(*, std::complex); +ARITHMETIC_OPERATIONS_REVERSE(/, std::complex); +ARITHMETIC_OPERATIONS_REVERSE(+, std::complex); +ARITHMETIC_OPERATIONS_REVERSE(-, std::complex); + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index a2cc48a570..df9dac7452 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -26,7 +26,7 @@ class scalar_operator { private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. - std::optional> m_constant_value; + std::optional> constant_value; /// @brief The function that generates the value of the scalar operator. /// The function can take a vector of complex-valued arguments @@ -34,78 +34,55 @@ class scalar_operator { ScalarCallbackFunction generator; public: - scalar_operator(double value) - : m_constant_value(value), generator() {} + + // constructors and destructors + + scalar_operator(double value); /// @brief Constructor that just takes and returns a complex double value. /// @NOTE: This replicates the behavior of the python `scalar_operator::const` /// without the need for an extra member function. - scalar_operator(std::complex value) - : m_constant_value(value), generator() {} - + scalar_operator(std::complex value); - scalar_operator(const ScalarCallbackFunction &create) - : m_constant_value(), generator(create) {} + scalar_operator(const ScalarCallbackFunction &create); /// @brief Constructor that just takes a callback function with no /// arguments. - scalar_operator(ScalarCallbackFunction &&create) - : m_constant_value() { - generator = std::move(create); - } + scalar_operator(ScalarCallbackFunction &&create); // copy constructor - scalar_operator(const scalar_operator &other) - : m_constant_value(other.m_constant_value), generator(other.generator) {} + scalar_operator(const scalar_operator &other); // move constructor - scalar_operator(scalar_operator &&other) - : m_constant_value(other.m_constant_value) { - generator = std::move(other.generator); - } + scalar_operator(scalar_operator &&other); + + ~scalar_operator() = default; + + // assignments // assignment operator - scalar_operator& operator=(const scalar_operator &other) { - if (this != &other) { - m_constant_value = other.m_constant_value; - generator = other.generator; - } - return *this; - } + scalar_operator& operator=(const scalar_operator &other); // move assignment operator - scalar_operator& operator=(scalar_operator &&other) { - if (this != &other) { - m_constant_value = other.m_constant_value; - generator = std::move(other.generator); - } - return *this; - } + scalar_operator& operator=(scalar_operator &&other); - /// NOTE: We should revisit these constructors and remove any that have - /// become unnecessary as the implementation improves. - // scalar_operator() = default; - // Copy constructor. - // scalar_operator(const scalar_operator &other); - // scalar_operator(scalar_operator &other); + // comparison - ~scalar_operator() = default; + bool operator==(scalar_operator other); - // Need this property for consistency with other inherited types. - // Particularly, to be used when the scalar operator is held within - // a variant type next to elementary operators. - std::vector degrees = {}; + // evaluations /// @brief Return the scalar operator as a concrete complex value. std::complex - evaluate(const std::map> parameters) const; + evaluate(const std::map> parameters = {}) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions, - const std::map> parameters) const; + matrix_2 to_matrix(const std::map dimensions = {}, + const std::map> parameters = {}) const; + + // right-hand arithmetics - // Arithmetic overloads against other operator types. scalar_operator operator*(double other) const; scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; @@ -132,6 +109,17 @@ class scalar_operator { scalar_operator& operator-=(const scalar_operator &other); /// TODO: implement and test pow + friend scalar_operator operator*(scalar_operator &&self, double other); + friend scalar_operator operator/(scalar_operator &&self, double other); + friend scalar_operator operator+(scalar_operator &&self, double other); + friend scalar_operator operator-(scalar_operator &&self, double other); + friend scalar_operator operator+(scalar_operator &&self, std::complex other); + friend scalar_operator operator/(scalar_operator &&self, std::complex other); + friend scalar_operator operator+(scalar_operator &&self, std::complex other); + friend scalar_operator operator-(scalar_operator &&self, std::complex other); + + // left-hand arithmetics + friend scalar_operator operator*(double other, const scalar_operator &self); friend scalar_operator operator/(double other, const scalar_operator &self); friend scalar_operator operator+(double other, const scalar_operator &self); @@ -140,16 +128,6 @@ class scalar_operator { friend scalar_operator operator/(std::complex other, const scalar_operator &self); friend scalar_operator operator+(std::complex other, const scalar_operator &self); friend scalar_operator operator-(std::complex other, const scalar_operator &self); - - // /// @brief Returns true if other is a scalar operator with the same - // /// generator. - bool operator==(scalar_operator other) { - if (this->m_constant_value.has_value() && other.m_constant_value.has_value()) { - return this->m_constant_value == other.m_constant_value; - } else { - throw std::runtime_error("not implemented"); - } - } }; diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index a88d3c7b38..53556ce83a 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -109,7 +109,7 @@ void assert_product_equal(const cudaq::product_operator)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); - ASSERT_TRUE(got.get_coefficient().evaluate({}) == expected_coefficient); + ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); } @@ -273,8 +273,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate({}), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 57e8ead2ad..4ae7c55b74 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -242,12 +242,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -285,7 +285,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -326,12 +326,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } } @@ -369,8 +369,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - std::cout << "GOT: " << term.get_coefficient().evaluate({}) << std::endl; - ASSERT_TRUE(term.get_coefficient().evaluate({}) == std::complex(2.)); + std::cout << "GOT: " << term.get_coefficient().evaluate() << std::endl; + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } } @@ -408,12 +408,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } @@ -453,7 +453,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.term_count() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.term_count() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate({}) == value); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 64367c6779..2a585e86a6 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -351,15 +351,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); ASSERT_TRUE(product_op.term_count() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); - ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == value_0); + ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -372,15 +372,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); ASSERT_TRUE(product_op.term_count() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); - ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -393,7 +393,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); ASSERT_TRUE(product_op.term_count() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate({}) == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; @@ -401,8 +401,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.term_count() == 2); ASSERT_TRUE(reverse.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); - ASSERT_TRUE(reverse.get_coefficient().evaluate({}) == scalar_op.evaluate({})); + ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -416,7 +416,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= value_0; ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == value_0); + ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -429,7 +429,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= 2.0; ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -443,8 +443,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= scalar_op; ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate({}) == scalar_op.evaluate({})); - ASSERT_TRUE(scalar_op.evaluate({}) == std::complex(0.1)); + ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); + ASSERT_TRUE(scalar_op.evaluate() == std::complex(0.1)); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index b6d237eba4..c3ef485228 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -31,10 +31,10 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_1 + scalar_op; // function + scalar_op; auto reverse_order_op = scalar_op + value_1; - EXPECT_NEAR(std::abs(scalar_op.evaluate({})), std::abs(value_0), 1e-5); + EXPECT_NEAR(std::abs(scalar_op.evaluate()), std::abs(value_0), 1e-5); - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_1 + value_0; EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); @@ -42,7 +42,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { // Checking composition of many scalar operators. auto third_op = new_scalar_op + reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value + want_value), 1e-5); } @@ -74,15 +74,15 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_3 - scalar_op; auto reverse_order_op = scalar_op - value_3; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op - reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); auto want_value = (value_3 - value_1) - (value_1 - value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -114,15 +114,15 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_3 * scalar_op; auto reverse_order_op = scalar_op * value_3; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op * reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); auto want_value = (value_3 * value_2) * (value_2 * value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -154,15 +154,15 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto new_scalar_op = value_3 / scalar_op; auto reverse_order_op = scalar_op / value_3; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 / value_3), 1e-5); // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; - auto got_value_third = third_op.evaluate({}); + auto got_value_third = third_op.evaluate(); auto want_value = (value_3 / value_2) / (value_2 / value_3); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value), 1e-5); } @@ -192,7 +192,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_0); scalar_op += value_0; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); } @@ -210,7 +210,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_0); scalar_op -= value_0; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); } @@ -228,7 +228,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op *= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); } @@ -246,7 +246,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op /= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); } @@ -293,8 +293,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op + scalar_op; auto reverse_order_op = scalar_op + other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_1 + value_0; EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); @@ -327,8 +327,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op - scalar_op; auto reverse_order_op = scalar_op - other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_1 - value_2; EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); @@ -361,8 +361,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op * scalar_op; auto reverse_order_op = scalar_op * other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_3 * value_2; auto reverse_want_value = value_2 * value_3; @@ -396,8 +396,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op / scalar_op; auto reverse_order_op = scalar_op / other_scalar_op; - auto got_value = new_scalar_op.evaluate({}); - auto got_value_1 = reverse_order_op.evaluate({}); + auto got_value = new_scalar_op.evaluate(); + auto got_value_1 = reverse_order_op.evaluate(); auto want_value = value_2 / value_0; auto reverse_want_value = value_0 / value_2; @@ -429,7 +429,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto other = cudaq::scalar_operator(value_0); scalar_op += other; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_0), 1e-5); } @@ -455,7 +455,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto scalar_op = cudaq::scalar_operator(value_0); scalar_op -= value_0; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_0 - value_0), 1e-5); } @@ -473,7 +473,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op *= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 * value_3), 1e-5); } @@ -491,7 +491,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto scalar_op = cudaq::scalar_operator(value_2); scalar_op /= value_3; - auto got_value = scalar_op.evaluate({}); + auto got_value = scalar_op.evaluate(); EXPECT_NEAR(std::abs(got_value), std::abs(value_2 / value_3), 1e-5); } diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index 158ab49841..5ec80350f4 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -24,10 +24,10 @@ TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { auto operator_2 = cudaq::scalar_operator(value_2); auto operator_3 = cudaq::scalar_operator(value_3); - auto got_value_0 = operator_0.evaluate({}); - auto got_value_1 = operator_1.evaluate({}); - auto got_value_2 = operator_2.evaluate({}); - auto got_value_3 = operator_3.evaluate({}); + auto got_value_0 = operator_0.evaluate(); + auto got_value_1 = operator_1.evaluate(); + auto got_value_2 = operator_2.evaluate(); + auto got_value_3 = operator_3.evaluate(); EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); @@ -78,10 +78,10 @@ TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { auto operator_2 = cudaq::scalar_operator(value_2); auto operator_3 = cudaq::scalar_operator(value_3); - auto got_value_0 = operator_0.evaluate({}); - auto got_value_1 = operator_1.evaluate({}); - auto got_value_2 = operator_2.evaluate({}); - auto got_value_3 = operator_3.evaluate({}); + auto got_value_0 = operator_0.evaluate(); + auto got_value_1 = operator_1.evaluate(); + auto got_value_2 = operator_2.evaluate(); + auto got_value_3 = operator_3.evaluate(); EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); From b50ae07fbe5c5d86988385da5451e3b173f77c30 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 28 Jan 2025 16:33:05 +0000 Subject: [PATCH 180/311] some clean up of op sum Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 321 +++++++++++------- runtime/cudaq/dynamics/product_operators.cpp | 2 +- runtime/cudaq/dynamics/scalar_operators.cpp | 20 +- runtime/cudaq/operators.h | 169 +++------ .../dynamics/elementary_ops_arithmetic.cpp | 52 +-- unittests/dynamics/operator_sum.cpp | 92 ++--- .../dynamics/product_operators_arithmetic.cpp | 86 ++--- 7 files changed, 376 insertions(+), 366 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index f2c7b75311..4ea1d3dcde 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -11,130 +11,215 @@ #include #include +#include +#include namespace cudaq { -// std::vector> -// operator_sum::canonicalize_product(product_operator &prod) const { -// std::vector> -// canonicalized_terms; - -// std::vector all_degrees; -// std::vector scalars; -// std::vector non_scalars; - -// for (const auto &op : prod.get_operators()) { -// if (std::holds_alternative(op)) { -// scalars.push_back(*std::get(op)); -// } else { -// non_scalars.push_back(*std::get(op)); -// all_degrees.insert(all_degrees.end(), -// std::get(op).degrees.begin(), -// std::get(op).degrees.end()); -// } -// } - -// if (all_degrees.size() == -// std::set(all_degrees.begin(), all_degrees.end()).size()) { -// std::sort(non_scalars.begin(), non_scalars.end(), -// [](const HandlerTy &a, const HandlerTy &b) { -// return a.degrees < b.degrees; -// }); -// } - -// for (size_t i = 0; std::min(scalars.size(), non_scalars.size()); i++) { -// canonicalized_terms.push_back(std::make_tuple(scalars[i], non_scalars[i])); -// } - -// return canonicalized_terms; -// } - -// std::vector> -// operator_sum::_canonical_terms() const { -// std::vector> terms; -// // for (const auto &term : terms) { -// // auto canonicalized = canonicalize_product(term); -// // terms.insert(terms.end(), canonicalized.begin(), canonicalized.end()); -// // } - -// // std::sort(terms.begin(), terms.end(), [](const auto &a, const auto &b) { -// // // return std::to_string(product_operator(a)) < -// // // std::to_string(product_operator(b)); -// // return product_operator(a).to_string() < -// product_operator(b).to_string(); -// // }); - -// return terms; -// } - -// operator_sum operator_sum::canonicalize() const { -// std::vector canonical_terms; -// for (const auto &term : _canonical_terms()) { -// canonical_terms.push_back(product_operator(term)); -// } -// return operator_sum(canonical_terms); -// } - -// bool operator_sum::operator==(const operator_sum &other) const { -// return _canonical_terms() == other._canonical_terms(); -// } - -// // Degrees property -// std::vector operator_sum::degrees() const { -// std::set unique_degrees; -// for (const auto &term : terms) { -// for (const auto &op : term.get_operators()) { -// unique_degrees.insert(op.get_degrees().begin(), -// op.get_degrees().end()); -// } -// } - -// return std::vector(unique_degrees.begin(), unique_degrees.end()); -// } - -// // Parameters property -// std::map operator_sum::parameters() const { -// std::map param_map; -// for (const auto &term : terms) { -// for (const auto &op : term.get_operators()) { -// auto op_params = op.parameters(); -// param_map.insert(op_params.begin(), op.params.end()); -// } -// } - -// return param_map; -// } - -// // Check if all terms are spin operators -// bool operator_sum::_is_spinop() const { -// return std::all_of( -// terms.begin(), terms.end(), [](product_operator &term) { -// return std::all_of(term.get_operators().begin(), -// term.get_operators().end(), -// [](const Operator &op) { return op.is_spinop(); -// }); -// }); -// } +// private methods + +template +requires std::derived_from +std::vector> operator_sum::canonicalize_product(product_operator &prod) const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +std::vector> operator_sum::_canonical_terms() const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +void operator_sum::aggregate_terms(const product_operator &head) { + this->terms.push_back(head.terms[0]); + this->coefficients.push_back(head.coefficients[0]); +} + +template +requires std::derived_from +template +void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { + this->terms.push_back(head.terms[0]); + this->coefficients.push_back(head.coefficients[0]); + aggregate_terms(std::forward(args)...); +} + +template +std::vector> operator_sum::canonicalize_product(product_operator &prod) const; + +template +std::vector> operator_sum::_canonical_terms() const; + +template +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2); + +template +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); + +// read-only properties + +template +requires std::derived_from +std::vector operator_sum::degrees() const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +int operator_sum::n_terms() const { + return this->terms.size(); +} + +template +requires std::derived_from +std::vector> operator_sum::get_terms() const { + std::vector> prods; + prods.reserve(this->terms.size()); + for (size_t i = 0; i < this->terms.size(); ++i) { + prods.push_back(product_operator(this->coefficients[i], this->terms[i])); + } + return prods; +} + +template +std::vector operator_sum::degrees() const; + +template +int operator_sum::n_terms() const; + +template +std::vector> operator_sum::get_terms() const; + +// constructors + +template +requires std::derived_from +template +operator_sum::operator_sum(const Args&... args) { + this->terms.reserve(sizeof...(Args)); + this->coefficients.reserve(sizeof...(Args)); + aggregate_terms(args...); +} + +template +requires std::derived_from +operator_sum::operator_sum(const std::vector> &terms) { + this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(term.terms[0]); + this->coefficients.push_back(term.coefficients[0]); + } +} + +template +requires std::derived_from +operator_sum::operator_sum(std::vector> &&terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(std::move(term.terms[0])); + this->coefficients.push_back(std::move(term.coefficients[0])); + } +} + +template +requires std::derived_from +operator_sum::operator_sum(const operator_sum &other) + : coefficients(other.coefficients), terms(other.terms) {} + +template +requires std::derived_from +operator_sum::operator_sum(operator_sum &&other) + : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} + +template +operator_sum::operator_sum(const product_operator &item1); + +template +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2); + +template +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); + +template +operator_sum::operator_sum(const std::vector> &terms); + +template +operator_sum::operator_sum(std::vector> &&terms); + +template +operator_sum::operator_sum(const operator_sum &other); + +template +operator_sum::operator_sum(operator_sum &&other); + +// assignments + +template +requires std::derived_from +operator_sum& operator_sum::operator=(const operator_sum &other) { + if (this != &other) { + coefficients = other.coefficients; + terms = other.terms; + } + return *this; +} + +template +requires std::derived_from +operator_sum& operator_sum::operator=(operator_sum &&other) { + if (this != &other) { + coefficients = std::move(other.coefficients); + terms = std::move(other.terms); + } + return *this; +} + +template +operator_sum& operator_sum::operator=(const operator_sum& other); + +template +operator_sum& operator_sum::operator=(operator_sum &&other); // evaluations -/// FIXME: -// tensor -// operator_sum::to_matrix(const std::map &dimensions, -// const std::map ¶ms) const { -// // todo -// } - -// std::string operator_sum::to_string() const { -// std::string result; -// // for (const auto &term : terms) { -// // result += term.to_string() + " + "; -// // } -// // // Remove last " + " -// // if (!result.empty()) -// // result.pop_back(); -// return result; -// } +template +requires std::derived_from +std::string operator_sum::to_string() const { + throw std::runtime_error("not implemented"); +} +template +requires std::derived_from +matrix_2 operator_sum::to_matrix(const std::map &dimensions, + const std::map ¶ms) const { + throw std::runtime_error("not implemented"); +} + +template +std::string operator_sum::to_string() const; + +template +matrix_2 operator_sum::to_matrix(const std::map &dimensions, + const std::map ¶ms) const; + +// comparisons + +template +requires std::derived_from +bool operator_sum::operator==(const operator_sum &other) const { + throw std::runtime_error("not implemented"); +} + +template +bool operator_sum::operator==(const operator_sum &other) const; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 356fe4b25b..53fc7acaf4 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -148,7 +148,7 @@ template<> matrix_2 product_operator::to_matrix( std::map dimensions, std::map> parameters) { - if (this->get_coefficient() != scalar_operator(1.) || this->term_count() != 1) + if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) throw std::runtime_error("not implemented"); return this->get_terms()[0].to_matrix(dimensions, parameters); } diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index f7f43536e7..f453fbf62f 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -56,16 +56,6 @@ scalar_operator& scalar_operator::operator=(scalar_operator &&other) { return *this; } -// comparison - -bool scalar_operator::operator==(scalar_operator other) { - if (this->constant_value.has_value() && other.constant_value.has_value()) { - return this->constant_value == other.constant_value; - } else { - throw std::runtime_error("not implemented"); - } -} - // evaluations std::complex scalar_operator::evaluate( @@ -82,6 +72,16 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +// comparison + +bool scalar_operator::operator==(scalar_operator other) { + if (this->constant_value.has_value() && other.constant_value.has_value()) { + return this->constant_value == other.constant_value; + } else { + throw std::runtime_error("not implemented"); + } +} + // right-hand arithmetics #define ARITHMETIC_OPERATIONS(op, otherTy) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index df9dac7452..68b78c0a9c 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -66,10 +66,6 @@ class scalar_operator { // move assignment operator scalar_operator& operator=(scalar_operator &&other); - // comparison - - bool operator==(scalar_operator other); - // evaluations /// @brief Return the scalar operator as a concrete complex value. @@ -81,6 +77,10 @@ class scalar_operator { matrix_2 to_matrix(const std::map dimensions = {}, const std::map> parameters = {}) const; + // comparisons + + bool operator==(scalar_operator other); + // right-hand arithmetics scalar_operator operator*(double other) const; @@ -140,23 +140,17 @@ requires std::derived_from class operator_sum { private: + std::vector> canonicalize_product(product_operator &prod) const; std::vector> _canonical_terms() const; - void aggregate_terms(const product_operator& head) { - terms.push_back(head.terms[0]); - coefficients.push_back(head.coefficients[0]); - } + void aggregate_terms(const product_operator& head); template - void aggregate_terms(const product_operator &head, Args&& ... args) { - terms.push_back(head.terms[0]); - coefficients.push_back(head.coefficients[0]); - aggregate_terms(std::forward(args)...); - } + void aggregate_terms(const product_operator &head, Args&& ... args); protected: @@ -166,73 +160,43 @@ class operator_sum { public: - /// @brief Construct a `cudaq::operator_sum` given a sequence of - /// `cudaq::product_operator`'s. - /// This operator expression represents a sum of terms, where each term - /// is a product of elementary and scalar operators. + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + /// @brief Return the number of operator terms that make up this operator sum. + int n_terms() const; + + std::vector> get_terms() const; + + // constructors and destructors + template, Args>...>::value, void>> - operator_sum(const Args&... args) { - terms.reserve(sizeof...(Args)); - coefficients.reserve(sizeof...(Args)); - aggregate_terms(args...); - } + operator_sum(const Args&... args); - operator_sum(const std::vector>& terms) { - this->terms.reserve(terms.size()); - this->coefficients.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(term.terms[0]); - this->coefficients.push_back(term.coefficients[0]); - } - } + operator_sum(const std::vector> &terms); - operator_sum(std::vector>&& terms) { - this->terms.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(std::move(term.terms[0])); - this->coefficients.push_back(std::move(term.coefficients[0])); - } - } + operator_sum(std::vector> &&terms); // copy constructor - operator_sum(const operator_sum &other) - : coefficients(other.coefficients), terms(other.terms) {} + operator_sum(const operator_sum &other); // move constructor - operator_sum(operator_sum &&other) - : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} - - // assignment operator - operator_sum& operator=(const operator_sum& other) { - if (this != &other) { - coefficients = other.coefficients; - terms = other.terms; - } - return *this; - } - - // move assignment operator - operator_sum& operator=(operator_sum &&other) { - if (this != &other) { - coefficients = std::move(other.coefficients); - terms = std::move(other.terms); - } - return *this; - } + operator_sum(operator_sum &&other); ~operator_sum() = default; - operator_sum canonicalize() const; + // assignments - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees() const; + // assignment operator + operator_sum& operator=(const operator_sum &other); - bool _is_spinop() const; + // move assignment operator + operator_sum& operator=(operator_sum &&other); - /// TODO: implement - // template - // TEval _evaluate(OperatorArithmetics &arithmetics) const; + // evaluations /// @brief Return the operator_sum as a string. std::string to_string() const; @@ -248,7 +212,21 @@ class operator_sum { const std::map &dimensions, const std::map> ¶ms = {}) const; - // Arithmetic operators + // comparisons + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms `blockwise`; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(const operator_sum &other) const; + + // right-hand arithmetics + operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; @@ -285,59 +263,6 @@ class operator_sum { operator_sum& operator*=(const operator_sum &other); operator_sum& operator+=(const operator_sum &other); operator_sum& operator-=(const operator_sum &other); - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-template-friend" -#endif -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-template-friend" -#endif -/* - friend operator_sum operator*(double other, const operator_sum &self); - friend operator_sum operator+(double other, const operator_sum &self); - friend operator_sum operator-(double other, const operator_sum &self); - friend operator_sum operator*(std::complex other, const operator_sum &self); - friend operator_sum operator+(std::complex other, const operator_sum &self); - friend operator_sum operator-(std::complex other, const operator_sum &self); - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - friend operator_sum operator*(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator+(const HandlerTy &other, const operator_sum &self); - friend operator_sum operator-(const HandlerTy &other, const operator_sum &self); -*/ -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic pop -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - /// @brief Return the number of operator terms that make up this operator sum. - int term_count() const { return terms.size(); } - - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. - bool operator==(const operator_sum &other) const; - - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: - std::vector> get_terms() const { - std::vector> prods; - prods.reserve(terms.size()); - for (size_t i = 0; i < terms.size(); ++i) { - prods.push_back(product_operator(coefficients[i], terms[i])); - } - return prods; } }; /// @brief Represents an operator expression consisting of a product of @@ -428,7 +353,7 @@ class product_operator : public operator_sum { /// @brief Return the number of operator terms that make up this product /// operator. - int term_count() const { return operator_sum::terms[0].size(); } + int n_terms() const { return operator_sum::terms[0].size(); } /// @brief Return the `product_operator` as a string. std::string to_string() const; diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 53556ce83a..57bd633e40 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -136,8 +136,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto reverse = other + self; // Check the `operator_sum` attributes. - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -163,8 +163,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto sum = self + other; auto reverse = other + self; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -191,8 +191,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -218,8 +218,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -306,7 +306,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self + other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -324,7 +324,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self + other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -346,7 +346,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self - other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -364,7 +364,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `operator_sum` type. auto sum = self - other; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -386,7 +386,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `product_operator` type. auto product = self * other; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -404,7 +404,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Produces an `product_operator` type. auto product = self * other; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -438,8 +438,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto got = self + operator_sum; auto reverse = operator_sum + self; - ASSERT_TRUE(got.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -471,8 +471,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto got = self - operator_sum; auto reverse = operator_sum - self; - ASSERT_TRUE(got.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -504,14 +504,14 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto got = self * operator_sum; auto reverse = operator_sum * self; - ASSERT_TRUE(got.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(got.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto &term : got.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); for (auto &term : reverse.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -539,7 +539,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { cudaq::elementary_operator::identity(1); operator_sum += cudaq::elementary_operator::annihilate(0); - ASSERT_TRUE(operator_sum.term_count() == 3); + ASSERT_TRUE(operator_sum.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -563,7 +563,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { cudaq::elementary_operator::identity(1); operator_sum -= cudaq::elementary_operator::annihilate(0); - ASSERT_TRUE(operator_sum.term_count() == 3); + ASSERT_TRUE(operator_sum.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -590,10 +590,10 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { operator_sum *= self; - ASSERT_TRUE(operator_sum.term_count() == 2); + ASSERT_TRUE(operator_sum.n_terms() == 2); for (auto &term : operator_sum.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 4ae7c55b74..282ddd46d6 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -237,16 +237,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto product = sum * cudaq::scalar_operator(0.1); auto reverse = cudaq::scalar_operator(0.1) * sum; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -259,8 +259,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto sum = original + cudaq::scalar_operator(1.0); auto reverse = cudaq::scalar_operator(1.0) + original; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -271,8 +271,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto difference = original - cudaq::scalar_operator(1.0); auto reverse = cudaq::scalar_operator(1.0) - original; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum *= scalar_operator` @@ -282,9 +282,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { sum *= cudaq::scalar_operator(0.1); - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); } } @@ -296,7 +296,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { sum += cudaq::scalar_operator(1.0); - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= scalar_operator` @@ -306,7 +306,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { sum -= cudaq::scalar_operator(1.0); - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } } @@ -321,16 +321,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * 2.0; auto reverse = 2.0 * sum; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } } @@ -343,8 +343,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + 2.0; auto reverse = 2.0 + original; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum - double` and `double - operator_sum` @@ -355,8 +355,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - 2.0; auto reverse = 2.0 - original; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum *= double` @@ -366,9 +366,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= 2.0; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); std::cout << "GOT: " << term.get_coefficient().evaluate() << std::endl; ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); } @@ -381,7 +381,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += 2.0; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= double` @@ -391,7 +391,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= 2.0; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum * std::complex` and `std::complex * @@ -403,16 +403,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * value; auto reverse = value * sum; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } @@ -426,8 +426,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + value; auto reverse = value + original; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum - std::complex` and `std::complex - @@ -439,8 +439,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - value; auto reverse = value - original; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `operator_sum *= std::complex` @@ -450,9 +450,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= value; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 1); + ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } } @@ -464,7 +464,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += value; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= std::complex` @@ -474,7 +474,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= value; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } } @@ -489,7 +489,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto sum = sum_0 + sum_1; - ASSERT_TRUE(sum.term_count() == 5); + ASSERT_TRUE(sum.n_terms() == 5); } // `operator_sum - operator_sum` @@ -502,7 +502,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto difference = sum_0 - sum_1; - ASSERT_TRUE(difference.term_count() == 5); + ASSERT_TRUE(difference.n_terms() == 5); } // `operator_sum * operator_sum` @@ -515,9 +515,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto sum_product = sum_0 * sum_1; - ASSERT_TRUE(sum_product.term_count() == 6); + ASSERT_TRUE(sum_product.n_terms() == 6); for (auto term : sum_product.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); } // `operator_sum *= operator_sum` @@ -530,9 +530,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { sum *= sum_1; - ASSERT_TRUE(sum.term_count() == 6); + ASSERT_TRUE(sum.n_terms() == 6); for (auto term : sum.get_terms()) - ASSERT_TRUE(term.term_count() == 2); + ASSERT_TRUE(term.n_terms() == 2); } } @@ -549,7 +549,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum += product; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum -= product_operator` @@ -561,7 +561,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum -= product; - ASSERT_TRUE(sum.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); } // `operator_sum *= product_operator` @@ -573,10 +573,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum *= product; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.term_count() == 3); + ASSERT_TRUE(term.n_terms() == 3); } } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 2a585e86a6..9b399be7f1 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -255,8 +255,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = value_0 + product_op; auto reverse = product_op + value_0; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); @@ -271,8 +271,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = 2.0 + product_op; auto reverse = product_op + 2.0; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); @@ -288,8 +288,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = scalar_op + product_op; auto reverse = product_op + scalar_op; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); @@ -304,8 +304,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = value_0 - product_op; auto reverse = product_op - value_0; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); @@ -320,8 +320,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = 2.0 - product_op; auto reverse = product_op - 2.0; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); @@ -337,8 +337,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = scalar_op - product_op; auto reverse = product_op - scalar_op; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); @@ -350,14 +350,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); @@ -371,14 +371,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); @@ -392,15 +392,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - ASSERT_TRUE(product_op.term_count() == 2); + ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); @@ -415,7 +415,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= value_0; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; @@ -428,7 +428,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::elementary_operator::annihilate(1); product *= 2.0; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; @@ -442,7 +442,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto scalar_op = cudaq::scalar_operator(0.1); product *= scalar_op; - ASSERT_TRUE(product.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(scalar_op.evaluate() == std::complex(0.1)); @@ -462,7 +462,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto sum = term_0 + term_1; - ASSERT_TRUE(sum.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); } // `product_operator - product_operator` @@ -474,7 +474,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto difference = term_0 - term_1; - ASSERT_TRUE(difference.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); } // `product_operator * product_operator` @@ -486,7 +486,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto product = term_0 * term_1; - ASSERT_TRUE(product.term_count() == 4); + ASSERT_TRUE(product.n_terms() == 4); } // `product_operator *= product_operator` @@ -498,7 +498,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { term_0 *= term_1; - ASSERT_TRUE(term_0.term_count() == 4); + ASSERT_TRUE(term_0.n_terms() == 4); } } @@ -513,8 +513,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto sum = product + elementary; auto reverse = elementary + product; - ASSERT_TRUE(sum.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); } // `product_operator - elementary_operator` @@ -526,8 +526,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto difference = product - elementary; auto reverse = elementary - product; - ASSERT_TRUE(difference.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); } // `product_operator * elementary_operator` @@ -539,8 +539,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto product = term_0 * elementary; auto reverse = elementary * term_0; - ASSERT_TRUE(product.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(product.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `product_operator *= elementary_operator` @@ -551,7 +551,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { product *= elementary; - ASSERT_TRUE(product.term_count() == 3); + ASSERT_TRUE(product.n_terms() == 3); } } @@ -567,8 +567,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto sum = product + original_sum; auto reverse = original_sum + product; - ASSERT_TRUE(sum.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `product_operator - operator_sum` @@ -581,8 +581,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto difference = product - original_sum; auto reverse = original_sum - product; - ASSERT_TRUE(difference.term_count() == 3); - ASSERT_TRUE(reverse.term_count() == 3); + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); } // `product_operator * operator_sum` @@ -595,15 +595,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto product = original_product * sum; auto reverse = sum * original_product; - ASSERT_TRUE(product.term_count() == 2); - ASSERT_TRUE(reverse.term_count() == 2); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.term_count() == 3); + ASSERT_TRUE(term.n_terms() == 3); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.term_count() == 3); + ASSERT_TRUE(term.n_terms() == 3); } } } From d8c1689df2be41171a6131bba86065e3d9dfbf50 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 28 Jan 2025 18:59:47 +0000 Subject: [PATCH 181/311] some clean up for products Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 7 +- runtime/cudaq/dynamics/product_operators.cpp | 326 +++++++++++-------- runtime/cudaq/operators.h | 167 +++------- 3 files changed, 242 insertions(+), 258 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 4ea1d3dcde..ad3ec284b3 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -32,10 +32,7 @@ std::vector> operator_sum::_ca template requires std::derived_from -void operator_sum::aggregate_terms(const product_operator &head) { - this->terms.push_back(head.terms[0]); - this->coefficients.push_back(head.coefficients[0]); -} +void operator_sum::aggregate_terms() {} template requires std::derived_from @@ -52,6 +49,8 @@ std::vector> operator_sum> operator_sum::_canonical_terms() const; +// no constructor for a single product, since that one should remain a product op + template void operator_sum::aggregate_terms(const product_operator &item1, const product_operator &item2); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 53fc7acaf4..458157ed13 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -16,153 +16,205 @@ namespace cudaq { -// tensor kroneckerHelper(std::vector &matrices) { -// // essentially we pass in the list of elementary operators to -// // this function -- with lowest degree being leftmost -- then it computes -// the -// // kronecker product of all of them. -// auto kronecker = [](tensor self, tensor other) { -// return self.kronecker(other); -// }; - -// return std::accumulate(begin(matrices), end(matrices), -// tensor::identity(1, 1), kronecker); -// } - -matrix_2 product_operator::to_matrix( - const std::map dimensions, - const std::map> parameters) const { - // Lambda functions to retrieve degrees and matrices - auto getDegrees = [](auto &&term) { return term.degrees; }; - auto getMatrix = [&](auto &&term) { - return term.to_matrix(dimensions, parameters); - }; - - // Initialize a result matrix with a single identity element - matrix_2 result(1, 1); - result[{0, 0}] = 1.0; - - // Iterate over all terms in the product operator - for (const auto &term : m_terms) { - // Get the degrees for the current term - auto termDegrees = std::visit(getDegrees, term); - bool inserted = false; - - matrix_2 termMatrix(1, 1); - termMatrix[{0, 0}] = 1.0; - - // Build the matrix list with identities or operator matrices - for (const auto &[degree, dim] : dimensions) { - if (std::find(termDegrees.begin(), termDegrees.end(), degree) != - termDegrees.end() && - !inserted) { - // Use the operator matrix for the active degree - termMatrix.kronecker_inplace(std::visit(getMatrix, term)); - inserted = true; - } else { - // Use identity matrix for other degrees - matrix_2 identityMatrix(dim, dim); - for (std::size_t i = 0; i < dim; i++) { - identityMatrix[{i, i}] = 1.0; - } - termMatrix.kronecker_inplace(identityMatrix); - } - } - - // Multiply the result matrix by the term matrix - result *= termMatrix; +// private methods + +template +requires std::derived_from +void product_operator::aggregate_terms() {} + +template +requires std::derived_from +template +void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { + this->terms[0].push_back(head); + aggregate_terms(std::forward(args)...); +} + +template +void product_operator::aggregate_terms(const elementary_operator &item1, + const elementary_operator &item2); + +template +void product_operator::aggregate_terms(const elementary_operator &item1, + const elementary_operator &item2, + const elementary_operator &item3); + +// read-only properties + +template +requires std::derived_from +std::vector product_operator::degrees() const { + std::set unsorted_degrees; + for (const HandlerTy &term : this->terms[0]) { + unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); } + auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION + return degrees; +} + +template +requires std::derived_from +int product_operator::n_terms() const { + return this->terms[0].size(); +} + +template +requires std::derived_from +std::vector product_operator::get_terms() const { + return this->terms[0]; +} + +template +requires std::derived_from +scalar_operator product_operator::get_coefficient() const { + return this->coefficients[0]; +} + +template +std::vector product_operator::degrees() const; + +template +int product_operator::n_terms() const; + +template +std::vector product_operator::get_terms() const; + +template +scalar_operator product_operator::get_coefficient() const; + +// constructors + +template +requires std::derived_from +template +product_operator::product_operator(scalar_operator coefficient, const Args&... args) { + this->coefficients.push_back(std::move(coefficient)); + std::vector ops = {}; + ops.reserve(sizeof...(Args)); + this->terms.push_back(ops); + aggregate_terms(args...); +} + +template +requires std::derived_from +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) { + this->terms.push_back(atomic_operators); + this->coefficients.push_back(std::move(coefficient)); +} + +template +requires std::derived_from +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) { + this->terms.push_back(std::move(atomic_operators)); + this->coefficients.push_back(std::move(coefficient)); +} + +template +requires std::derived_from +product_operator::product_operator(const product_operator &other) { + this->terms = other.terms; + this->coefficients = other.coefficients; +} - return result; +template +requires std::derived_from +product_operator::product_operator(product_operator &&other) { + this->terms = std::move(other.terms); + this->coefficients = std::move(other.coefficients); } -// /// IMPLEMENT: -// tensor product_operator::to_matrix( -// std::map dimensions, -// std::map> parameters) { - -// /// TODO: This initial logic may not be needed. -// // std::vector degrees, levels; -// // for(std::map::iterator it = dimensions.begin(); it != -// // dimensions.end(); ++it) { -// // degrees.push_back(it->first); -// // levels.push_back(it->second); -// // } -// // // Calculate the size of the full Hilbert space of the given product -// // operator. int fullSize = std::accumulate(begin(levels), end(levels), 1, -// // std::multiplies()); -// std::cout << "here 49\n"; -// auto getDegrees = [](auto &&t) { return t.degrees; }; -// auto getMatrix = [&](auto &&t) { -// auto outMatrix = t.to_matrix(dimensions, parameters); -// std::cout << "dumping the outMatrix : \n"; -// outMatrix.dump(); -// return outMatrix; -// }; -// std::vector matricesFullVectorSpace; -// for (auto &term : ops) { -// auto op_degrees = std::visit(getDegrees, term); -// std::cout << "here 58\n"; -// // Keeps track of if we've already inserted the operator matrix -// // into the full list of matrices. -// bool alreadyInserted = false; -// std::vector matrixWithIdentities; -// /// General procedure for inserting identities: -// // * check if the operator acts on this degree by looking through -// // `op_degrees` -// // * if not, insert an identity matrix of the proper level size -// // * if so, insert the matrix itself -// for (auto [degree, level] : dimensions) { -// std::cout << "here 68\n"; -// auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); -// if (it != op_degrees.end() && !alreadyInserted) { -// std::cout << "here 71\n"; -// auto matrix = std::visit(getMatrix, term); -// std::cout << "here 75\n"; -// matrixWithIdentities.push_back(matrix); -// std::cout << "here 77\n"; -// } else { -// std::cout << "here 80\n"; -// matrixWithIdentities.push_back(tensor::identity(level, -// level)); -// } -// } -// std::cout << "here 84\n"; -// matricesFullVectorSpace.push_back(kroneckerHelper(matrixWithIdentities)); -// } -// // Now just need to accumulate with matrix multiplication all of the -// // matrices in `matricesFullVectorSpace` -- they should all be the same -// size -// // already. -// std::cout << "here 89\n"; - -// // temporary -// auto out = tensor::identity(1, 1); -// std::cout << "here 93\n"; -// return out; -// } - - -// FIXME: remove - to be replaced with the general implementation for product op -template<> -matrix_2 product_operator::to_matrix( - std::map dimensions, - std::map> parameters) { +template +product_operator::product_operator(scalar_operator coefficient); + +template +product_operator::product_operator(scalar_operator coefficient, + const elementary_operator &item1); + +template +product_operator::product_operator(scalar_operator coefficient, + const elementary_operator &item1, + const elementary_operator &item2); + +template +product_operator::product_operator(scalar_operator coefficient, + const elementary_operator &item1, + const elementary_operator &item2, + const elementary_operator &item3); + +template +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + +template +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + +template +product_operator::product_operator(const product_operator &other); + +template +product_operator::product_operator(product_operator &&other); + +// assignments + +template +requires std::derived_from +product_operator& product_operator::operator=(const product_operator &other) { + if (this != &other) { + this->terms = other.terms; + this->coefficients = other.coefficients; + } + return *this; +} + +template +requires std::derived_from +product_operator& product_operator::operator=(product_operator &&other) { + if (this != &other) { + this->coefficients = std::move(other.coefficients); + this->terms = std::move(other.terms); + } + return *this; +} + +template +product_operator& product_operator::operator=(const product_operator &other); + +template +product_operator& product_operator::operator=(product_operator &&other); + +// evaluations + +template +requires std::derived_from +std::string product_operator::to_string() const { + throw std::runtime_error("not implemented"); +} + +template +requires std::derived_from +matrix_2 product_operator::to_matrix(std::map dimensions, + std::map> parameters) const { if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) throw std::runtime_error("not implemented"); return this->get_terms()[0].to_matrix(dimensions, parameters); } +template +std::string product_operator::to_string() const; -// Degrees property -template -std::vector product_operator::degrees() const { - std::set unique_degrees; - for (const HandlerTy &term : this->get_terms()) { - unique_degrees.insert(term.degrees.begin(), term.degrees.end()); - } - // FIXME: SORT THE DEGREES - return std::vector(unique_degrees.begin(), unique_degrees.end()); +template +matrix_2 product_operator::to_matrix(std::map dimensions, + std::map> parameters) const; + +// comparisons + +template +requires std::derived_from +bool product_operator::operator==(const product_operator &other) const { + throw std::runtime_error("not implemented"); } +template +bool product_operator::operator==(const product_operator &other) const; + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 68b78c0a9c..02337caabb 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -147,7 +147,7 @@ class operator_sum { std::vector> _canonical_terms() const; - void aggregate_terms(const product_operator& head); + void aggregate_terms(); template void aggregate_terms(const product_operator &head, Args&& ... args); @@ -208,9 +208,8 @@ class operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix( - const std::map &dimensions, - const std::map> ¶ms = {}) const; + matrix_2 to_matrix(const std::map &dimensions = {}, + const std::map ¶ms = {}) const; // comparisons @@ -265,6 +264,7 @@ class operator_sum { operator_sum& operator-=(const operator_sum &other); }; + /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types @@ -275,85 +275,53 @@ class product_operator : public operator_sum { private: - void aggregate_terms(const HandlerTy& head) { - operator_sum::terms[0].push_back(head); - } + void aggregate_terms(); template - void aggregate_terms(const HandlerTy &head, Args&& ... args) { - operator_sum::terms[0].push_back(head); - aggregate_terms(std::forward(args)...); - } + void aggregate_terms(const HandlerTy &head, Args&& ... args); public: - product_operator(scalar_operator coefficient) { - operator_sum::terms.push_back({}); - operator_sum::coefficients.push_back(std::move(coefficient)); - } + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + std::vector degrees() const; + + /// @brief Return the number of operator terms that make up this product + /// operator. + int n_terms() const; + + std::vector get_terms() const; + + scalar_operator get_coefficient() const; + + // constructors and destructors - // Constructor for an operator expression that represents a product - // of scalar and elementary operators. - // arg atomic_operators : The operators of which to compute the product when - // evaluating the operator expression. template...>::value, void>> - product_operator(scalar_operator coefficient, const Args&... args) { - operator_sum::coefficients.push_back(std::move(coefficient)); - std::vector ops = {}; - ops.reserve(sizeof...(Args)); - operator_sum::terms.push_back(ops); - aggregate_terms(args...); - } + product_operator(scalar_operator coefficient, const Args&... args); - product_operator(scalar_operator coefficient, const std::vector& atomic_operators) { - operator_sum::terms.push_back(atomic_operators); - operator_sum::coefficients.push_back(std::move(coefficient)); - } + product_operator(scalar_operator coefficient, const std::vector &atomic_operators); - product_operator(scalar_operator coefficient, std::vector&& atomic_operators) { - operator_sum::terms.push_back(std::move(atomic_operators)); - operator_sum::coefficients.push_back(std::move(coefficient)); - } + product_operator(scalar_operator coefficient, std::vector &&atomic_operators); // copy constructor - product_operator(const product_operator &other) { - operator_sum::terms = other.terms; - operator_sum::coefficients = other.coefficients; - } + product_operator(const product_operator &other); // move constructor - product_operator(product_operator &&other) { - operator_sum::terms = std::move(other.terms); - operator_sum::coefficients = std::move(other.coefficients); - } + product_operator(product_operator &&other); - // assignment operator - product_operator& operator=(const product_operator& other) { - if (this != &other) { - operator_sum::terms = other.terms; - operator_sum::coefficients = other.coefficients; - } - return *this; - } + ~product_operator() = default; - // move assignment operator - product_operator& operator=(product_operator &&other) { - if (this != &other) { - this->coefficients = std::move(other.coefficients); - this->terms = std::move(other.terms); - } - return *this; - } + // assignments - ~product_operator() = default; + // assignment operator + product_operator& operator=(const product_operator &other); - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees() const; + // move assignment operator + product_operator& operator=(product_operator &&other); - /// @brief Return the number of operator terms that make up this product - /// operator. - int n_terms() const { return operator_sum::terms[0].size(); } + // evaluations /// @brief Return the `product_operator` as a string. std::string to_string() const; @@ -365,11 +333,24 @@ class product_operator : public operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 - to_matrix(const std::map dimensions, - const std::map> parameters) const; + matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; + + // comparisons + + /// @brief True, if the other value is an operator_sum with equivalent terms, + /// and False otherwise. The equality takes into account that operator + /// addition is commutative, as is the product of two operators if they + /// act on different degrees of freedom. + /// The equality comparison does *not* take commutation relations into + /// account, and does not try to reorder terms `blockwise`; it may hence + /// evaluate to False, even if two operators in reality are the same. + /// If the equality evaluates to True, on the other hand, the operators + /// are guaranteed to represent the same transformation for all arguments. + bool operator==(const product_operator &other) const; + + // right-hand arithmetics - // Arithmetic overloads against all other operator types. product_operator operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; @@ -393,54 +374,6 @@ class product_operator : public operator_sum { operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wnon-template-friend" -#endif -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wnon-template-friend" -#endif -/* - friend product_operator operator*(double other, const product_operator &self); - friend operator_sum operator+(double other, const product_operator &self); - friend operator_sum operator-(double other, const product_operator &self); - friend product_operator operator*(std::complex other, const product_operator &self); - friend operator_sum operator+(std::complex other, const product_operator &self); - friend operator_sum operator-(std::complex other, const product_operator &self); - friend product_operator operator*(const scalar_operator &other, const product_operator &self); - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - friend product_operator operator*(const HandlerTy &other, const product_operator &self); - friend operator_sum operator+(const HandlerTy &other, const product_operator &self); - friend operator_sum operator-(const HandlerTy &other, const product_operator &self); -*/ -#if (defined(__GNUC__) && !defined(__clang__) && !defined(__INTEL_COMPILER)) -#pragma GCC diagnostic pop -#endif -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. - bool operator==(product_operator other); - - /// FIXME: Protect this once I can do deeper testing in `unittests`. - // protected: - std::vector get_terms() const { - return operator_sum::terms[0]; } - - scalar_operator get_coefficient() const { - return operator_sum::coefficients[0]; } }; From 5c932b1532ecc022b5a3054db126fc8ce0015e34 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 11:08:01 +0000 Subject: [PATCH 182/311] no type constraints for HandlerTy Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 2 +- .../{instantiations.cpp => arithmetics.cpp} | 87 +------------------ ...{template_declarations.h => arithmetics.h} | 26 ------ runtime/cudaq/dynamics/operator_sum.cpp | 17 ---- runtime/cudaq/dynamics/product_operators.cpp | 16 ---- runtime/cudaq/operators.h | 4 +- 6 files changed, 4 insertions(+), 148 deletions(-) rename runtime/cudaq/dynamics/{instantiations.cpp => arithmetics.cpp} (85%) rename runtime/cudaq/dynamics/{template_declarations.h => arithmetics.h} (82%) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 3bbfb4eec0..45bedd7d65 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -22,7 +22,7 @@ set(CUDAQ_OPS_SRC elementary_operators.cpp product_operators.cpp operator_sum.cpp - instantiations.cpp + arithmetics.cpp schedule.cpp ) diff --git a/runtime/cudaq/dynamics/instantiations.cpp b/runtime/cudaq/dynamics/arithmetics.cpp similarity index 85% rename from runtime/cudaq/dynamics/instantiations.cpp rename to runtime/cudaq/dynamics/arithmetics.cpp index 77741ac1a9..609575e502 100644 --- a/runtime/cudaq/dynamics/instantiations.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -20,61 +20,51 @@ namespace cudaq { // product operator left-hand arithmetics template -requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self) { return product_operator(other * std::move(self.get_coefficient()), std::move(self.get_terms())); } template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator({other}), self); + return operator_sum(product_operator(other), self); } template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator({other}), self * (-1.)); + return operator_sum(product_operator(other), self * (-1.)); } template -requires std::derived_from product_operator operator*(double other, const product_operator &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(double other, const product_operator &self) { return operator_sum(product_operator({scalar_operator(other)}), self); } template -requires std::derived_from operator_sum operator-(double other, const product_operator &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from product_operator operator*(const std::complex other, const product_operator &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(const std::complex other, const product_operator &self) { return operator_sum(product_operator({scalar_operator(other)}), self); } template -requires std::derived_from operator_sum operator-(const std::complex other, const product_operator &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self) { std::vector terms = std::move(self.get_terms()); terms.insert(terms.begin(), other); @@ -82,13 +72,11 @@ product_operator operator*(const HandlerTy &other, const product_oper } template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self) { return operator_sum(product_operator(1., other), self); } template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const product_operator &self) { return (self * (-1.)) + other; } @@ -96,7 +84,6 @@ operator_sum operator-(const HandlerTy &other, const product_operator // operator sum left-hand arithmetics template -requires std::derived_from operator_sum operator*(const scalar_operator &other, const operator_sum &self) { std::vector> terms = self.get_terms(); for (auto &term : terms) @@ -105,7 +92,6 @@ operator_sum operator*(const scalar_operator &other, const operator_s } template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self) { std::vector> terms = self.get_terms(); terms.insert(terms.begin(), product_operator(other)); @@ -113,7 +99,6 @@ operator_sum operator+(const scalar_operator &other, const operator_s } template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self) { auto negative_self = self * (-1.); std::vector> terms = negative_self.get_terms(); @@ -122,55 +107,46 @@ operator_sum operator-(const scalar_operator &other, const operator_s } template -requires std::derived_from operator_sum operator*(double other, const operator_sum &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(double other, const operator_sum &self) { return self + scalar_operator(other); } template -requires std::derived_from operator_sum operator-(double other, const operator_sum &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from operator_sum operator*(std::complex other, const operator_sum &self) { return self * scalar_operator(other); } template -requires std::derived_from operator_sum operator+(std::complex other, const operator_sum &self) { return self + scalar_operator(other); } template -requires std::derived_from operator_sum operator-(std::complex other, const operator_sum &self) { return (self * (-1.)) + scalar_operator(other); } template -requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self) { return ((operator_sum)product_operator(1., other)) * self; } template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self) { return ((operator_sum)product_operator(1., other)) + self; } template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self) { return ((operator_sum)product_operator(1., other)) - self; } @@ -178,85 +154,72 @@ operator_sum operator-(const HandlerTy &other, const operator_sum -requires std::derived_from operator_sum operator_sum::operator*(double other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator+(double other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator-(double other) const { return *this - scalar_operator(other); } template -requires std::derived_from operator_sum& operator_sum::operator*=(double other) { *this *= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(double other) { *this += scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(double other) { *this -= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(std::complex other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator+(std::complex other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum operator_sum::operator-(std::complex other) const { return *this - scalar_operator(other); } template -requires std::derived_from operator_sum& operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(std::complex other) { *this += scalar_operator(other); return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(std::complex other) { *this -= scalar_operator(other); return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(const scalar_operator &other) const { std::vector> combined_terms = std::move(this->get_terms()); for (auto &term : combined_terms) { @@ -266,7 +229,6 @@ operator_sum operator_sum::operator*(const scalar_operator } template -requires std::derived_from operator_sum operator_sum::operator+(const scalar_operator &other) const { // FIXME: reserve length auto combined_terms = std::move(this->get_terms()); @@ -275,34 +237,29 @@ operator_sum operator_sum::operator+(const scalar_operator } template -requires std::derived_from operator_sum operator_sum::operator-(const scalar_operator &other) const { return *this + (other * (-1.0)); } template -requires std::derived_from operator_sum& operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const scalar_operator &other) { *this = *this + other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(const scalar_operator &other) { *this = *this - other; return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(const HandlerTy &other) const { std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { @@ -312,7 +269,6 @@ operator_sum operator_sum::operator*(const HandlerTy &othe } template -requires std::derived_from operator_sum operator_sum::operator+(const HandlerTy &other) const { auto combined_terms = std::move(this->get_terms()); combined_terms.push_back(product_operator(1., other)); @@ -320,7 +276,6 @@ operator_sum operator_sum::operator+(const HandlerTy &othe } template -requires std::derived_from operator_sum operator_sum::operator-(const HandlerTy &other) const { std::vector> combined_terms = std::move(this->get_terms()); combined_terms.push_back(product_operator(-1., other)); @@ -328,14 +283,12 @@ operator_sum operator_sum::operator-(const HandlerTy &othe } template -requires std::derived_from operator_sum& operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const HandlerTy &other) { this->coefficients.push_back(1.); this->terms.push_back({other}); @@ -343,7 +296,6 @@ operator_sum& operator_sum::operator+=(const HandlerTy &ot } template -requires std::derived_from operator_sum& operator_sum::operator-=(const HandlerTy &other) { this->coefficients.push_back(-1.); this->terms.push_back({other}); @@ -351,7 +303,6 @@ operator_sum& operator_sum::operator-=(const HandlerTy &ot } template -requires std::derived_from operator_sum operator_sum::operator*(const product_operator &other) const { std::vector> combined_terms = this->get_terms(); for (auto &term : combined_terms) { @@ -361,7 +312,6 @@ operator_sum operator_sum::operator*(const product_operato } template -requires std::derived_from operator_sum operator_sum::operator+(const product_operator &other) const { std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other); @@ -369,7 +319,6 @@ operator_sum operator_sum::operator+(const product_operato } template -requires std::derived_from operator_sum operator_sum::operator-(const product_operator &other) const { std::vector> combined_terms = this->get_terms(); combined_terms.push_back(other * (-1.)); @@ -377,28 +326,24 @@ operator_sum operator_sum::operator-(const product_operato } template -requires std::derived_from operator_sum& operator_sum::operator*=(const product_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const product_operator &other) { *this = *this + other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(const product_operator &other) { *this = *this - other; return *this; } template -requires std::derived_from operator_sum operator_sum::operator*(const operator_sum &other) const { auto self_terms = this->get_terms(); std::vector> product_terms; @@ -412,7 +357,6 @@ operator_sum operator_sum::operator*(const operator_sum -requires std::derived_from operator_sum operator_sum::operator+(const operator_sum &other) const { std::vector> combined_terms = std::move(this->get_terms()); std::vector> other_terms = std::move(other.get_terms()); @@ -421,27 +365,23 @@ operator_sum operator_sum::operator+(const operator_sum -requires std::derived_from operator_sum operator_sum::operator-(const operator_sum &other) const { return *this + (other * (-1)); } template -requires std::derived_from operator_sum& operator_sum::operator*=(const operator_sum &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator-=(const operator_sum &other) { *this = *this - other; return *this; } template -requires std::derived_from operator_sum& operator_sum::operator+=(const operator_sum &other) { *this = *this + other; return *this; @@ -451,84 +391,71 @@ operator_sum& operator_sum::operator+=(const operator_sum< // product operator right-hand arithmetics template -requires std::derived_from product_operator product_operator::operator*(double other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator+(double other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator-(double other) const { return *this - scalar_operator(other); } template -requires std::derived_from product_operator& product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } template -requires std::derived_from product_operator product_operator::operator*(std::complex other) const { return *this * scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator+(std::complex other) const { return *this + scalar_operator(other); } template -requires std::derived_from operator_sum product_operator::operator-(std::complex other) const { return *this - scalar_operator(other); } template -requires std::derived_from product_operator& product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } template -requires std::derived_from product_operator product_operator::operator*(const scalar_operator &other) const { return product_operator(this->coefficients[0] * other, this->terms[0]); } template -requires std::derived_from operator_sum product_operator::operator+(const scalar_operator &other) const { product_operator coefficient(other); return operator_sum(coefficient, *this); } template -requires std::derived_from operator_sum product_operator::operator-(const scalar_operator &other) const { product_operator coefficient(-1. * other); return operator_sum(coefficient, *this); } template -requires std::derived_from product_operator& product_operator::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from product_operator product_operator::operator*(const HandlerTy &other) const { auto combined_terms = this->terms[0]; combined_terms.push_back(other); @@ -536,26 +463,22 @@ product_operator product_operator::operator*(const Handler } template -requires std::derived_from operator_sum product_operator::operator+(const HandlerTy &other) const { return operator_sum(*this, product_operator(1., other)); } template -requires std::derived_from operator_sum product_operator::operator-(const HandlerTy &other) const { return operator_sum(*this, product_operator(-1., other)); } template -requires std::derived_from product_operator& product_operator::operator*=(const HandlerTy &other) { *this = *this * other; return *this; } template -requires std::derived_from product_operator product_operator::operator*(const product_operator &other) const { auto combined_terms = this->terms[0]; combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); @@ -563,26 +486,22 @@ product_operator product_operator::operator*(const product } template -requires std::derived_from operator_sum product_operator::operator+(const product_operator &other) const { return operator_sum(*this, other); } template -requires std::derived_from operator_sum product_operator::operator-(const product_operator &other) const { return operator_sum(*this, other * (-1.)); } template -requires std::derived_from product_operator& product_operator::operator*=(const product_operator &other) { *this = *this * other; return *this; } template -requires std::derived_from operator_sum product_operator::operator*(const operator_sum &other) const { std::vector> other_terms = other.get_terms(); for (auto &term : other_terms) { @@ -592,7 +511,6 @@ operator_sum product_operator::operator*(const operator_su } template -requires std::derived_from operator_sum product_operator::operator+(const operator_sum &other) const { std::vector other_terms = other.get_terms(); other_terms.insert(other_terms.begin(), *this); @@ -600,7 +518,6 @@ operator_sum product_operator::operator+(const operator_su } template -requires std::derived_from operator_sum product_operator::operator-(const operator_sum &other) const { auto negative_other = other * (-1.); std::vector> other_terms = negative_other.get_terms(); diff --git a/runtime/cudaq/dynamics/template_declarations.h b/runtime/cudaq/dynamics/arithmetics.h similarity index 82% rename from runtime/cudaq/dynamics/template_declarations.h rename to runtime/cudaq/dynamics/arithmetics.h index e1cec53736..8100562c9f 100644 --- a/runtime/cudaq/dynamics/template_declarations.h +++ b/runtime/cudaq/dynamics/arithmetics.h @@ -17,85 +17,59 @@ class scalar_operator; class elementary_operator; template -requires std::derived_from class product_operator; template -requires std::derived_from class operator_sum; template -requires std::derived_from product_operator operator*(double other, const product_operator &self); template -requires std::derived_from operator_sum operator+(double other, const product_operator &self); template -requires std::derived_from operator_sum operator-(double other, const product_operator &self); template -requires std::derived_from product_operator operator*(std::complex other, const product_operator &self); template -requires std::derived_from operator_sum operator+(std::complex other, const product_operator &self); template -requires std::derived_from operator_sum operator-(std::complex other, const product_operator &self); template -requires std::derived_from product_operator operator*(const scalar_operator &other, const product_operator &self); template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const product_operator &self); template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const product_operator &self); template -requires std::derived_from product_operator operator*(const HandlerTy &other, const product_operator &self); template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const product_operator &self); template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const product_operator &self); template -requires std::derived_from operator_sum operator*(double other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(double other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(double other, const operator_sum &self); template -requires std::derived_from operator_sum operator*(std::complex other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(std::complex other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(std::complex other, const operator_sum &self); template -requires std::derived_from operator_sum operator*(const scalar_operator &other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(const scalar_operator &other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -requires std::derived_from operator_sum operator*(const HandlerTy &other, const operator_sum &self); template -requires std::derived_from operator_sum operator+(const HandlerTy &other, const operator_sum &self); template -requires std::derived_from operator_sum operator-(const HandlerTy &other, const operator_sum &self); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index ad3ec284b3..237b35e09b 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -19,23 +19,19 @@ namespace cudaq { // private methods template -requires std::derived_from std::vector> operator_sum::canonicalize_product(product_operator &prod) const { throw std::runtime_error("not implemented"); } template -requires std::derived_from std::vector> operator_sum::_canonical_terms() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from void operator_sum::aggregate_terms() {} template -requires std::derived_from template void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { this->terms.push_back(head.terms[0]); @@ -63,19 +59,16 @@ void operator_sum::aggregate_terms(const product_operator -requires std::derived_from std::vector operator_sum::degrees() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from int operator_sum::n_terms() const { return this->terms.size(); } template -requires std::derived_from std::vector> operator_sum::get_terms() const { std::vector> prods; prods.reserve(this->terms.size()); @@ -97,7 +90,6 @@ std::vector> operator_sum -requires std::derived_from template operator_sum::operator_sum(const Args&... args) { this->terms.reserve(sizeof...(Args)); @@ -106,7 +98,6 @@ operator_sum::operator_sum(const Args&... args) { } template -requires std::derived_from operator_sum::operator_sum(const std::vector> &terms) { this->terms.reserve(terms.size()); this->coefficients.reserve(terms.size()); @@ -117,7 +108,6 @@ operator_sum::operator_sum(const std::vector -requires std::derived_from operator_sum::operator_sum(std::vector> &&terms) { this->terms.reserve(terms.size()); for (const product_operator& term : terms) { @@ -127,12 +117,10 @@ operator_sum::operator_sum(std::vector> & } template -requires std::derived_from operator_sum::operator_sum(const operator_sum &other) : coefficients(other.coefficients), terms(other.terms) {} template -requires std::derived_from operator_sum::operator_sum(operator_sum &&other) : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} @@ -163,7 +151,6 @@ operator_sum::operator_sum(operator_sum -requires std::derived_from operator_sum& operator_sum::operator=(const operator_sum &other) { if (this != &other) { coefficients = other.coefficients; @@ -173,7 +160,6 @@ operator_sum& operator_sum::operator=(const operator_sum -requires std::derived_from operator_sum& operator_sum::operator=(operator_sum &&other) { if (this != &other) { coefficients = std::move(other.coefficients); @@ -191,13 +177,11 @@ operator_sum& operator_sum::operator=( // evaluations template -requires std::derived_from std::string operator_sum::to_string() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from matrix_2 operator_sum::to_matrix(const std::map &dimensions, const std::map ¶ms) const { throw std::runtime_error("not implemented"); @@ -213,7 +197,6 @@ matrix_2 operator_sum::to_matrix(const std::map & // comparisons template -requires std::derived_from bool operator_sum::operator==(const operator_sum &other) const { throw std::runtime_error("not implemented"); } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 458157ed13..fdebec00d0 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -19,11 +19,9 @@ namespace cudaq { // private methods template -requires std::derived_from void product_operator::aggregate_terms() {} template -requires std::derived_from template void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { this->terms[0].push_back(head); @@ -42,7 +40,6 @@ void product_operator::aggregate_terms(const elementary_ope // read-only properties template -requires std::derived_from std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->terms[0]) { @@ -54,19 +51,16 @@ std::vector product_operator::degrees() const { } template -requires std::derived_from int product_operator::n_terms() const { return this->terms[0].size(); } template -requires std::derived_from std::vector product_operator::get_terms() const { return this->terms[0]; } template -requires std::derived_from scalar_operator product_operator::get_coefficient() const { return this->coefficients[0]; } @@ -86,7 +80,6 @@ scalar_operator product_operator::get_coefficient() const; // constructors template -requires std::derived_from template product_operator::product_operator(scalar_operator coefficient, const Args&... args) { this->coefficients.push_back(std::move(coefficient)); @@ -97,28 +90,24 @@ product_operator::product_operator(scalar_operator coefficient, const } template -requires std::derived_from product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) { this->terms.push_back(atomic_operators); this->coefficients.push_back(std::move(coefficient)); } template -requires std::derived_from product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) { this->terms.push_back(std::move(atomic_operators)); this->coefficients.push_back(std::move(coefficient)); } template -requires std::derived_from product_operator::product_operator(const product_operator &other) { this->terms = other.terms; this->coefficients = other.coefficients; } template -requires std::derived_from product_operator::product_operator(product_operator &&other) { this->terms = std::move(other.terms); this->coefficients = std::move(other.coefficients); @@ -157,7 +146,6 @@ product_operator::product_operator(product_operator -requires std::derived_from product_operator& product_operator::operator=(const product_operator &other) { if (this != &other) { this->terms = other.terms; @@ -167,7 +155,6 @@ product_operator& product_operator::operator=(const produc } template -requires std::derived_from product_operator& product_operator::operator=(product_operator &&other) { if (this != &other) { this->coefficients = std::move(other.coefficients); @@ -185,13 +172,11 @@ product_operator& product_operator::op // evaluations template -requires std::derived_from std::string product_operator::to_string() const { throw std::runtime_error("not implemented"); } template -requires std::derived_from matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const { if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) @@ -209,7 +194,6 @@ matrix_2 product_operator::to_matrix(std::map dim // comparisons template -requires std::derived_from bool product_operator::operator==(const product_operator &other) const { throw std::runtime_error("not implemented"); } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 02337caabb..70c6decc45 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,7 +8,7 @@ #pragma once -#include "dynamics/template_declarations.h" +#include "dynamics/arithmetics.h" #include "definition.h" #include "utils/tensor.h" @@ -136,7 +136,6 @@ class scalar_operator { /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. template // handler needs to inherit from operation_handler -requires std::derived_from class operator_sum { private: @@ -270,7 +269,6 @@ class operator_sum { /// quantum kernels, but they provide methods to convert them to data types /// that can. template // handler needs to inherit from operation_handler -requires std::derived_from class product_operator : public operator_sum { private: From 9f102e970e0e154103735aed6efeb060a5ab453f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 14:52:45 +0000 Subject: [PATCH 183/311] left hand arithmetics cleanup Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 184 ------------------ runtime/cudaq/dynamics/operator_sum.cpp | 145 +++++++++++++- runtime/cudaq/dynamics/product_operators.cpp | 90 +++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 10 + runtime/cudaq/operators.h | 74 +++++++ .../dynamics/elementary_ops_arithmetic.cpp | 2 +- 6 files changed, 317 insertions(+), 188 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index 609575e502..c481c45d25 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -17,140 +17,6 @@ namespace cudaq { -// product operator left-hand arithmetics - -template -product_operator operator*(const scalar_operator &other, const product_operator &self) { - return product_operator(other * std::move(self.get_coefficient()), std::move(self.get_terms())); -} - -template -operator_sum operator+(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator(other), self); -} - -template -operator_sum operator-(const scalar_operator &other, const product_operator &self) { - return operator_sum(product_operator(other), self * (-1.)); -} - -template -product_operator operator*(double other, const product_operator &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(double other, const product_operator &self) { - return operator_sum(product_operator({scalar_operator(other)}), self); -} - -template -operator_sum operator-(double other, const product_operator &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -product_operator operator*(const std::complex other, const product_operator &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(const std::complex other, const product_operator &self) { - return operator_sum(product_operator({scalar_operator(other)}), self); -} - -template -operator_sum operator-(const std::complex other, const product_operator &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -product_operator operator*(const HandlerTy &other, const product_operator &self) { - std::vector terms = std::move(self.get_terms()); - terms.insert(terms.begin(), other); - return product_operator(std::move(self.get_coefficient()), std::move(terms)); -} - -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(1., other), self); -} - -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return (self * (-1.)) + other; -} - -// operator sum left-hand arithmetics - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - for (auto &term : terms) - term = term * other; - return operator_sum(terms); -} - -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self) { - std::vector> terms = self.get_terms(); - terms.insert(terms.begin(), product_operator(other)); - return operator_sum(terms); -} - -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self) { - auto negative_self = self * (-1.); - std::vector> terms = negative_self.get_terms(); - terms.insert(terms.begin(), product_operator(other)); - return operator_sum(terms); -} - -template -operator_sum operator*(double other, const operator_sum &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(double other, const operator_sum &self) { - return self + scalar_operator(other); -} - -template -operator_sum operator-(double other, const operator_sum &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -operator_sum operator*(std::complex other, const operator_sum &self) { - return self * scalar_operator(other); -} - -template -operator_sum operator+(std::complex other, const operator_sum &self) { - return self + scalar_operator(other); -} - -template -operator_sum operator-(std::complex other, const operator_sum &self) { - return (self * (-1.)) + scalar_operator(other); -} - -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { - return ((operator_sum)product_operator(1., other)) * self; -} - -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self) { - return ((operator_sum)product_operator(1., other)) + self; -} - -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self) { - return ((operator_sum)product_operator(1., other)) - self; -} - // operator sum right-hand arithmetics template @@ -528,56 +394,6 @@ operator_sum product_operator::operator-(const operator_su // instantiations -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -product_operator operator*(const elementary_operator &other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator+(const elementary_operator &other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -operator_sum operator-(const elementary_operator &other, const product_operator &self); - -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); - template operator_sum operator_sum::operator*(double other) const; template diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 237b35e09b..23448cb5a8 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -45,7 +45,7 @@ std::vector> operator_sum> operator_sum::_canonical_terms() const; -// no constructor for a single product, since that one should remain a product op +// no overload for a single product, since we don't want a constructor for a single term template void operator_sum::aggregate_terms(const product_operator &item1, @@ -124,8 +124,7 @@ template operator_sum::operator_sum(operator_sum &&other) : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} -template -operator_sum::operator_sum(const product_operator &item1); +// no constructor for a single product, since that one should remain a product op template operator_sum::operator_sum(const product_operator &item1, @@ -204,4 +203,144 @@ bool operator_sum::operator==(const operator_sum &other) c template bool operator_sum::operator==(const operator_sum &other) const; +// unary operators + +template +operator_sum operator_sum::operator-() const { + std::vector coefficients; + coefficients.reserve(this->coefficients.size()); + for (auto coeff : this->coefficients) + coefficients.push_back(-1. * coeff); + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = this->terms; + return sum; +} + +template +operator_sum operator_sum::operator+() const { + return *this; +} + +template +operator_sum operator_sum::operator-() const; + +template +operator_sum operator_sum::operator+() const; + +// left-hand arithmetics + +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.coefficients.size()); \ + for (auto coeff : self.coefficients) \ + coefficients.push_back(coeff * other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = self.terms; \ + return sum; \ + } + +SUM_MULTIPLICATION_REVERSE(double); +SUM_MULTIPLICATION_REVERSE(std::complex); +SUM_MULTIPLICATION_REVERSE(const scalar_operator &); + +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(other); \ + for (auto coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + terms.push_back({}); \ + for (auto term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_REVERSE(double, +); +SUM_ADDITION_REVERSE(double, -); +SUM_ADDITION_REVERSE(std::complex, +); +SUM_ADDITION_REVERSE(std::complex, -); +SUM_ADDITION_REVERSE(const scalar_operator &, +); +SUM_ADDITION_REVERSE(const scalar_operator &, -); + +template +operator_sum operator*(const HandlerTy &other, const operator_sum &self) { + std::vector> terms; + terms.reserve(self.terms.size()); + for (auto term : self.terms) { + std::vector prod; + prod.reserve(term.size() + 1); + prod.push_back(other); + for (auto op : term) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = self.coefficients; + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy & other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(1.); \ + for (auto coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_HANDLER_REVERSE(+) +SUM_ADDITION_HANDLER_REVERSE(-) + +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator*(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); +template +operator_sum operator+(const elementary_operator &other, const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); +template +operator_sum operator-(std::complex other, const operator_sum &self); +template +operator_sum operator-(const elementary_operator &other, const operator_sum &self); + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index fdebec00d0..5182409045 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -201,4 +201,94 @@ bool product_operator::operator==(const product_operator & template bool product_operator::operator==(const product_operator &other) const; +// unary operators + +template +product_operator product_operator::operator-() const { + return product_operator(-1. * this->coefficients[0], this->terms[0]); +} + +template +product_operator product_operator::operator+() const { + return *this; +} + +template +product_operator product_operator::operator-() const; + +template +product_operator product_operator::operator+() const; + +// left-hand arithmetics + +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*(otherTy other, \ + const product_operator &self) { \ + return product_operator(other * self.coefficients[0], self.terms[0]); \ + } + +PRODUCT_MULTIPLICATION_REVERSE(double); +PRODUCT_MULTIPLICATION_REVERSE(std::complex); +PRODUCT_MULTIPLICATION_REVERSE(const scalar_operator &); + +#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const product_operator &self) { \ + return operator_sum(product_operator(other), op self); \ + } + +PRODUCT_ADDITION_REVERSE(double, +); +PRODUCT_ADDITION_REVERSE(double, -); +PRODUCT_ADDITION_REVERSE(std::complex, +); +PRODUCT_ADDITION_REVERSE(std::complex, -); +PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); +PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); + +template +product_operator operator*(const HandlerTy &other, const product_operator &self) { + std::vector terms; + terms.reserve(self.terms[0].size() + 1); + terms.push_back(other); + for (auto term : self.terms[0]) + terms.push_back(term); + return product_operator(self.coefficients[0], std::move(terms)); +} + +template +operator_sum operator+(const HandlerTy &other, const product_operator &self) { + return operator_sum(product_operator(1., other), self); +} + +template +operator_sum operator-(const HandlerTy &other, const product_operator &self) { + return operator_sum(product_operator(1., other), -self); +} + +template +product_operator operator*(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); +template +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(const elementary_operator &other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator+(std::complex other, const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +operator_sum operator+(const elementary_operator &other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +operator_sum operator-(std::complex other, const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +operator_sum operator-(const elementary_operator &other, const product_operator &self); + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index f453fbf62f..42fac4e7e6 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -82,6 +82,16 @@ bool scalar_operator::operator==(scalar_operator other) { } } +// unary operators + +scalar_operator scalar_operator::operator-() const { + return *this * (-1.); +} + +scalar_operator scalar_operator::operator+() const { + return *this; +} + // right-hand arithmetics #define ARITHMETIC_OPERATIONS(op, otherTy) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 70c6decc45..3f248bf407 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -19,6 +19,9 @@ #include #include +// FIXME: DO WE REALLY NEED THE HANDLERTY OVERLOADS? -> EVERYTHING SHOULD BE A PRODUCT +// might be perf relevant to avoid the creation of unnecessary products... + namespace cudaq { class scalar_operator { @@ -81,6 +84,11 @@ class scalar_operator { bool operator==(scalar_operator other); + // unary operators + + scalar_operator operator-() const; + scalar_operator operator+() const; + // right-hand arithmetics scalar_operator operator*(double other) const; @@ -223,6 +231,11 @@ class operator_sum { /// are guaranteed to represent the same transformation for all arguments. bool operator==(const operator_sum &other) const; + // unary operators + + operator_sum operator-() const; + operator_sum operator+() const; + // right-hand arithmetics operator_sum operator*(double other) const; @@ -261,6 +274,34 @@ class operator_sum { operator_sum& operator*=(const operator_sum &other); operator_sum& operator+=(const operator_sum &other); operator_sum& operator-=(const operator_sum &other); + + // left-hand arithmetics + + // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. + template + friend operator_sum operator*(double other, const operator_sum &self); + template + friend operator_sum operator+(double other, const operator_sum &self); + template + friend operator_sum operator-(double other, const operator_sum &self); + template + friend operator_sum operator*(std::complex other, const operator_sum &self); + template + friend operator_sum operator+(std::complex other, const operator_sum &self); + template + friend operator_sum operator-(std::complex other, const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator*(const T &other, const operator_sum &self); + template + friend operator_sum operator+(const T &other, const operator_sum &self); + template + friend operator_sum operator-(const T &other, const operator_sum &self); }; @@ -347,6 +388,11 @@ class product_operator : public operator_sum { /// are guaranteed to represent the same transformation for all arguments. bool operator==(const product_operator &other) const; + // unary operators + + product_operator operator-() const; + product_operator operator+() const; + // right-hand arithmetics product_operator operator*(double other) const; @@ -372,6 +418,34 @@ class product_operator : public operator_sum { operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; + + // left-hand arithmetics + + // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. + template + friend product_operator operator*(double other, const product_operator &self); + template + friend operator_sum operator+(double other, const product_operator &self); + template + friend operator_sum operator-(double other, const product_operator &self); + template + friend product_operator operator*(std::complex other, const product_operator &self); + template + friend operator_sum operator+(std::complex other, const product_operator &self); + template + friend operator_sum operator-(std::complex other, const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + template + friend product_operator operator*(const T &other, const product_operator &self); + template + friend operator_sum operator+(const T &other, const product_operator &self); + template + friend operator_sum operator-(const T &other, const product_operator &self); }; diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 57bd633e40..519f4d3a30 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -107,7 +107,7 @@ void assert_product_equal(const cudaq::product_operator &expected_coefficient, const std::vector &expected_terms) { - auto sumterms_prod = ((cudaq::operator_sum)got).get_terms(); + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); From 326b60702639deb0dd6cfc01fd33c58a7e2faf10 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 15:30:39 +0000 Subject: [PATCH 184/311] more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 187 ------------------- runtime/cudaq/dynamics/operator_sum.cpp | 115 +++++++++++- runtime/cudaq/dynamics/product_operators.cpp | 88 ++++++++- 3 files changed, 194 insertions(+), 196 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index c481c45d25..6010d478ea 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -19,21 +19,6 @@ namespace cudaq { // operator sum right-hand arithmetics -template -operator_sum operator_sum::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(double other) const { - return *this - scalar_operator(other); -} - template operator_sum& operator_sum::operator*=(double other) { *this *= scalar_operator(other); @@ -52,21 +37,6 @@ operator_sum& operator_sum::operator-=(double other) { return *this; } -template -operator_sum operator_sum::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum operator_sum::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum operator_sum::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - template operator_sum& operator_sum::operator*=(std::complex other) { *this *= scalar_operator(other); @@ -85,28 +55,6 @@ operator_sum& operator_sum::operator-=(std::complex -operator_sum operator_sum::operator*(const scalar_operator &other) const { - std::vector> combined_terms = std::move(this->get_terms()); - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const scalar_operator &other) const { - // FIXME: reserve length - auto combined_terms = std::move(this->get_terms()); - combined_terms.push_back(product_operator(other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const scalar_operator &other) const { - return *this + (other * (-1.0)); -} - template operator_sum& operator_sum::operator*=(const scalar_operator &other) { *this = *this * other; @@ -125,29 +73,6 @@ operator_sum& operator_sum::operator-=(const scalar_operat return *this; } -template -operator_sum operator_sum::operator*(const HandlerTy &other) const { - std::vector> combined_terms = this->get_terms(); - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const HandlerTy &other) const { - auto combined_terms = std::move(this->get_terms()); - combined_terms.push_back(product_operator(1., other)); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const HandlerTy &other) const { - std::vector> combined_terms = std::move(this->get_terms()); - combined_terms.push_back(product_operator(-1., other)); - return operator_sum(combined_terms); -} - template operator_sum& operator_sum::operator*=(const HandlerTy &other) { *this = *this * other; @@ -256,88 +181,24 @@ operator_sum& operator_sum::operator+=(const operator_sum< // product operator right-hand arithmetics -template -product_operator product_operator::operator*(double other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(double other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(double other) const { - return *this - scalar_operator(other); -} - template product_operator& product_operator::operator*=(double other) { *this = *this * scalar_operator(other); return *this; } -template -product_operator product_operator::operator*(std::complex other) const { - return *this * scalar_operator(other); -} - -template -operator_sum product_operator::operator+(std::complex other) const { - return *this + scalar_operator(other); -} - -template -operator_sum product_operator::operator-(std::complex other) const { - return *this - scalar_operator(other); -} - template product_operator& product_operator::operator*=(std::complex other) { *this = *this * scalar_operator(other); return *this; } -template -product_operator product_operator::operator*(const scalar_operator &other) const { - return product_operator(this->coefficients[0] * other, this->terms[0]); -} - -template -operator_sum product_operator::operator+(const scalar_operator &other) const { - product_operator coefficient(other); - return operator_sum(coefficient, *this); -} - -template -operator_sum product_operator::operator-(const scalar_operator &other) const { - product_operator coefficient(-1. * other); - return operator_sum(coefficient, *this); -} - template product_operator& product_operator::operator*=(const scalar_operator &other) { *this = *this * other; return *this; } -template -product_operator product_operator::operator*(const HandlerTy &other) const { - auto combined_terms = this->terms[0]; - combined_terms.push_back(other); - return product_operator(1., combined_terms); -} - -template -operator_sum product_operator::operator+(const HandlerTy &other) const { - return operator_sum(*this, product_operator(1., other)); -} - -template -operator_sum product_operator::operator-(const HandlerTy &other) const { - return operator_sum(*this, product_operator(-1., other)); -} - template product_operator& product_operator::operator*=(const HandlerTy &other) { *this = *this * other; @@ -394,12 +255,6 @@ operator_sum product_operator::operator-(const operator_su // instantiations -template -operator_sum operator_sum::operator*(double other) const; -template -operator_sum operator_sum::operator+(double other) const; -template -operator_sum operator_sum::operator-(double other) const; template operator_sum& operator_sum::operator*=(double other); template @@ -407,36 +262,18 @@ operator_sum& operator_sum::operator+= template operator_sum& operator_sum::operator-=(double other); template -operator_sum operator_sum::operator*(std::complex other) const; -template -operator_sum operator_sum::operator+(std::complex other) const; -template -operator_sum operator_sum::operator-(std::complex other) const; -template operator_sum& operator_sum::operator*=(std::complex other); template operator_sum& operator_sum::operator+=(std::complex other); template operator_sum& operator_sum::operator-=(std::complex other); template -operator_sum operator_sum::operator*(const scalar_operator &other) const; -template -operator_sum operator_sum::operator+(const scalar_operator &other) const; -template -operator_sum operator_sum::operator-(const scalar_operator &other) const; -template operator_sum& operator_sum::operator*=(const scalar_operator &other); template operator_sum& operator_sum::operator+=(const scalar_operator &other); template operator_sum& operator_sum::operator-=(const scalar_operator &other); template -operator_sum operator_sum::operator*(const elementary_operator &other) const; -template -operator_sum operator_sum::operator+(const elementary_operator &other) const; -template -operator_sum operator_sum::operator-(const elementary_operator &other) const; -template operator_sum& operator_sum::operator*=(const elementary_operator &other); template operator_sum& operator_sum::operator+=(const elementary_operator &other); @@ -467,37 +304,13 @@ operator_sum& operator_sum::operator-= template operator_sum& operator_sum::operator+=(const operator_sum &other); -template -product_operator product_operator::operator*(double other) const; -template -operator_sum product_operator::operator+(double other) const; -template -operator_sum product_operator::operator-(double other) const; template product_operator& product_operator::operator*=(double other); template -product_operator product_operator::operator*(std::complex other) const; -template -operator_sum product_operator::operator+(std::complex other) const; -template -operator_sum product_operator::operator-(std::complex other) const; -template product_operator& product_operator::operator*=(std::complex other); template -product_operator product_operator::operator*(const scalar_operator &other) const; -template -operator_sum product_operator::operator+(const scalar_operator &other) const; -template -operator_sum product_operator::operator-(const scalar_operator &other) const; -template product_operator& product_operator::operator*=(const scalar_operator &other); template -product_operator product_operator::operator*(const elementary_operator &other) const; -template -operator_sum product_operator::operator+(const elementary_operator &other) const; -template -operator_sum product_operator::operator-(const elementary_operator &other) const; -template product_operator& product_operator::operator*=(const elementary_operator &other); template product_operator product_operator::operator*(const product_operator &other) const; diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 23448cb5a8..2b4d3096e1 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -228,6 +228,119 @@ operator_sum operator_sum::operator-() template operator_sum operator_sum::operator+() const; +// right-hand arithmetics + +#define SUM_MULTIPLICATION(otherTy) \ + template \ + operator_sum operator_sum::operator*(otherTy other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size()); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(coeff * other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = this->terms; \ + return sum; \ + } + +SUM_MULTIPLICATION(double); +SUM_MULTIPLICATION(std::complex); +SUM_MULTIPLICATION(const scalar_operator &); + +#define SUM_ADDITION(otherTy, op) \ + template \ + operator_sum operator_sum::operator op(otherTy other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->terms.size() + 1); \ + coefficients.push_back(other); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + terms.push_back({}); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION(double, +); +SUM_ADDITION(double, -); +SUM_ADDITION(std::complex, +); +SUM_ADDITION(std::complex, -); +SUM_ADDITION(const scalar_operator &, +); +SUM_ADDITION(const scalar_operator &, -); + +template +operator_sum operator_sum::operator*(const HandlerTy &other) const { + std::vector> terms; + terms.reserve(this->terms.size()); + for (auto term : this->terms) { + std::vector prod; + prod.reserve(term.size() + 1); + for (auto op : term) + prod.push_back(op); + prod.push_back(other); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = this->coefficients; + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_HANDLER(op) \ + template \ + operator_sum operator_sum::operator op( \ + const HandlerTy &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->terms.size() + 1); \ + coefficients.push_back(1.); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_HANDLER(+) +SUM_ADDITION_HANDLER(-) + +template +operator_sum operator_sum::operator*(double other) const; +template +operator_sum operator_sum::operator+(double other) const; +template +operator_sum operator_sum::operator-(double other) const; +template +operator_sum operator_sum::operator*(std::complex other) const; +template +operator_sum operator_sum::operator+(std::complex other) const; +template +operator_sum operator_sum::operator-(std::complex other) const; +template +operator_sum operator_sum::operator*(const scalar_operator &other) const; +template +operator_sum operator_sum::operator+(const scalar_operator &other) const; +template +operator_sum operator_sum::operator-(const scalar_operator &other) const; +template +operator_sum operator_sum::operator*(const elementary_operator &other) const; +template +operator_sum operator_sum::operator+(const elementary_operator &other) const; +template +operator_sum operator_sum::operator-(const elementary_operator &other) const; + // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ @@ -295,7 +408,7 @@ operator_sum operator*(const HandlerTy &other, const operator_sum \ - operator_sum operator op(const HandlerTy & other, \ + operator_sum operator op(const HandlerTy &other, \ const operator_sum &self) { \ std::vector coefficients; \ coefficients.reserve(self.terms.size() + 1); \ diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 5182409045..5be196a52e 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -219,6 +219,78 @@ product_operator product_operator::ope template product_operator product_operator::operator+() const; +// right-hand arithmetics + +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficients[0], this->terms[0]); \ + } + +PRODUCT_MULTIPLICATION(double); +PRODUCT_MULTIPLICATION(std::complex); +PRODUCT_MULTIPLICATION(const scalar_operator &); + +#define PRODUCT_ADDITION(otherTy, op) \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const { \ + return operator_sum(product_operator(other), op *this); \ + } + +PRODUCT_ADDITION(double, +); +PRODUCT_ADDITION(double, -); +PRODUCT_ADDITION(std::complex, +); +PRODUCT_ADDITION(std::complex, -); +PRODUCT_ADDITION(const scalar_operator &, +); +PRODUCT_ADDITION(const scalar_operator &, -); + +template +product_operator product_operator::operator*(const HandlerTy &other) const { + std::vector terms; + terms.reserve(this->terms[0].size() + 1); + for (auto term : this->terms[0]) + terms.push_back(term); + terms.push_back(other); + return product_operator(this->coefficients[0], std::move(terms)); +} + +#define PRODUCT_ADDITION_HANDLER(op) \ + template \ + operator_sum product_operator::operator op( \ + const HandlerTy &other) const { \ + return operator_sum(product_operator(1., other), op *this); \ + } + +PRODUCT_ADDITION_HANDLER(+) +PRODUCT_ADDITION_HANDLER(-) + +template +product_operator product_operator::operator*(double other) const; +template +operator_sum product_operator::operator+(double other) const; +template +operator_sum product_operator::operator-(double other) const; +template +product_operator product_operator::operator*(std::complex other) const; +template +operator_sum product_operator::operator+(std::complex other) const; +template +operator_sum product_operator::operator-(std::complex other) const; +template +product_operator product_operator::operator*(const scalar_operator &other) const; +template +operator_sum product_operator::operator+(const scalar_operator &other) const; +template +operator_sum product_operator::operator-(const scalar_operator &other) const; +template +product_operator product_operator::operator*(const elementary_operator &other) const; +template +operator_sum product_operator::operator+(const elementary_operator &other) const; +template +operator_sum product_operator::operator-(const elementary_operator &other) const; + // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ @@ -256,15 +328,15 @@ product_operator operator*(const HandlerTy &other, const product_oper return product_operator(self.coefficients[0], std::move(terms)); } -template -operator_sum operator+(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(1., other), self); -} +#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy &other, \ + const product_operator &self) { \ + return operator_sum(product_operator(1., other), op self); \ + } -template -operator_sum operator-(const HandlerTy &other, const product_operator &self) { - return operator_sum(product_operator(1., other), -self); -} +PRODUCT_ADDITION_HANDLER_REVERSE(+) +PRODUCT_ADDITION_HANDLER_REVERSE(-) template product_operator operator*(double other, const product_operator &self); From 42908b0a9dd90bb1afcbae88a3dc20fd6e527cf1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 16:22:04 +0000 Subject: [PATCH 185/311] more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 53 ------------- runtime/cudaq/dynamics/product_operators.cpp | 82 +++++++++++++++++++- runtime/cudaq/operators.h | 9 ++- 3 files changed, 89 insertions(+), 55 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index 6010d478ea..7bb1d6d3fe 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -205,22 +205,6 @@ product_operator& product_operator::operator*=(const Handl return *this; } -template -product_operator product_operator::operator*(const product_operator &other) const { - auto combined_terms = this->terms[0]; - combined_terms.insert(combined_terms.end(), other.terms[0].begin(), other.terms[0].end()); - return product_operator(1., combined_terms); -} - -template -operator_sum product_operator::operator+(const product_operator &other) const { - return operator_sum(*this, other); -} - -template -operator_sum product_operator::operator-(const product_operator &other) const { - return operator_sum(*this, other * (-1.)); -} template product_operator& product_operator::operator*=(const product_operator &other) { @@ -228,30 +212,6 @@ product_operator& product_operator::operator*=(const produ return *this; } -template -operator_sum product_operator::operator*(const operator_sum &other) const { - std::vector> other_terms = other.get_terms(); - for (auto &term : other_terms) { - term = *this * term; - } - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator+(const operator_sum &other) const { - std::vector other_terms = other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - -template -operator_sum product_operator::operator-(const operator_sum &other) const { - auto negative_other = other * (-1.); - std::vector> other_terms = negative_other.get_terms(); - other_terms.insert(other_terms.begin(), *this); - return operator_sum(other_terms); -} - // instantiations @@ -313,18 +273,5 @@ product_operator& product_operator::op template product_operator& product_operator::operator*=(const elementary_operator &other); template -product_operator product_operator::operator*(const product_operator &other) const; -template -operator_sum product_operator::operator+(const product_operator &other) const; -template -operator_sum product_operator::operator-(const product_operator &other) const; -template product_operator& product_operator::operator*=(const product_operator &other); -template -operator_sum product_operator::operator*(const operator_sum &other) const; -template -operator_sum product_operator::operator+(const operator_sum &other) const; -template -operator_sum product_operator::operator-(const operator_sum &other) const; - } \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 5be196a52e..d0139a4039 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -260,7 +260,7 @@ product_operator product_operator::operator*(const Handler template \ operator_sum product_operator::operator op( \ const HandlerTy &other) const { \ - return operator_sum(product_operator(1., other), op *this); \ + return operator_sum(product_operator(op 1., other), *this); \ } PRODUCT_ADDITION_HANDLER(+) @@ -291,6 +291,86 @@ operator_sum product_operator::operato template operator_sum product_operator::operator-(const elementary_operator &other) const; +template +product_operator product_operator::operator*(const product_operator &other) const { + std::vector terms; + terms.reserve(this->terms[0].size() + other.terms[0].size()); + for (auto term : this->terms[0]) + terms.push_back(term); + for (auto term : other.terms[0]) + terms.push_back(term); + return product_operator(this->coefficients[0] * other.coefficients[0], std::move(terms)); +} + +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(op other, *this); \ + } + +PRODUCT_ADDITION_PRODUCT(+) +PRODUCT_ADDITION_PRODUCT(-) + +template +operator_sum product_operator::operator*(const operator_sum &other) const { + std::vector coefficients; + coefficients.reserve(other.coefficients.size()); + for (auto coeff : other.coefficients) + coefficients.push_back(this->coefficients[0] * coeff); + std::vector> terms; + terms.reserve(other.terms.size()); + for (auto term : other.terms) { + std::vector prod; + prod.reserve(this->terms[0].size() + term.size()); + for (auto op : this->terms[0]) + prod.push_back(op); + for (auto op : term) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = std::move(terms); + return sum; +} + +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(other.coefficients.size() + 1); \ + coefficients.push_back(this->coefficients[0]); \ + for (auto coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(other.terms.size() + 1); \ + terms.push_back(this->terms[0]); \ + for (auto term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +PRODUCT_ADDITION_SUM(+) +PRODUCT_ADDITION_SUM(-) + +template +product_operator product_operator::operator*(const product_operator &other) const; +template +operator_sum product_operator::operator+(const product_operator &other) const; +template +operator_sum product_operator::operator-(const product_operator &other) const; +template +operator_sum product_operator::operator*(const operator_sum &other) const; +template +operator_sum product_operator::operator+(const operator_sum &other) const; +template +operator_sum product_operator::operator-(const operator_sum &other) const; + // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 3f248bf407..f16c88270d 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -301,7 +301,14 @@ class operator_sum { template friend operator_sum operator+(const T &other, const operator_sum &self); template - friend operator_sum operator-(const T &other, const operator_sum &self); + friend operator_sum operator-(const T &other, const operator_sum &self); + + template + friend operator_sum product_operator::operator*(const operator_sum &other) const; + template + friend operator_sum product_operator::operator+(const operator_sum &other) const; + template + friend operator_sum product_operator::operator-(const operator_sum &other) const; }; From 666734d837b95551590f81c2eab07936dc160f73 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 16:59:22 +0000 Subject: [PATCH 186/311] more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/arithmetics.cpp | 61 ------------- runtime/cudaq/dynamics/operator_sum.cpp | 111 ++++++++++++++++++++++++ 2 files changed, 111 insertions(+), 61 deletions(-) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp index 7bb1d6d3fe..00ec426254 100644 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ b/runtime/cudaq/dynamics/arithmetics.cpp @@ -93,29 +93,6 @@ operator_sum& operator_sum::operator-=(const HandlerTy &ot return *this; } -template -operator_sum operator_sum::operator*(const product_operator &other) const { - std::vector> combined_terms = this->get_terms(); - for (auto &term : combined_terms) { - term *= other; - } - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator+(const product_operator &other) const { - std::vector> combined_terms = this->get_terms(); - combined_terms.push_back(other); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const product_operator &other) const { - std::vector> combined_terms = this->get_terms(); - combined_terms.push_back(other * (-1.)); - return operator_sum(combined_terms); -} - template operator_sum& operator_sum::operator*=(const product_operator &other) { *this = *this * other; @@ -134,32 +111,6 @@ operator_sum& operator_sum::operator-=(const product_opera return *this; } -template -operator_sum operator_sum::operator*(const operator_sum &other) const { - auto self_terms = this->get_terms(); - std::vector> product_terms; - auto other_terms = other.get_terms(); - for (auto &term : self_terms) { - for (auto &other_term : other_terms) { - product_terms.push_back(term * other_term); - } - } - return operator_sum(product_terms); -} - -template -operator_sum operator_sum::operator+(const operator_sum &other) const { - std::vector> combined_terms = std::move(this->get_terms()); - std::vector> other_terms = std::move(other.get_terms()); - combined_terms.insert(combined_terms.end(), std::make_move_iterator(other_terms.begin()), std::make_move_iterator(other_terms.end())); - return operator_sum(combined_terms); -} - -template -operator_sum operator_sum::operator-(const operator_sum &other) const { - return *this + (other * (-1)); -} - template operator_sum& operator_sum::operator*=(const operator_sum &other) { *this = *this * other; @@ -240,24 +191,12 @@ operator_sum& operator_sum::operator+= template operator_sum& operator_sum::operator-=(const elementary_operator &other); template -operator_sum operator_sum::operator*(const product_operator &other) const; -template -operator_sum operator_sum::operator+(const product_operator &other) const; -template -operator_sum operator_sum::operator-(const product_operator &other) const; -template operator_sum& operator_sum::operator*=(const product_operator &other); template operator_sum& operator_sum::operator+=(const product_operator &other); template operator_sum& operator_sum::operator-=(const product_operator &other); template -operator_sum operator_sum::operator*(const operator_sum &other) const; -template -operator_sum operator_sum::operator+(const operator_sum &other) const; -template -operator_sum operator_sum::operator-(const operator_sum &other) const; -template operator_sum& operator_sum::operator*=(const operator_sum &other); template operator_sum& operator_sum::operator-=(const operator_sum &other); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 2b4d3096e1..91f5e7187a 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -341,6 +341,117 @@ operator_sum operator_sum::operator+(c template operator_sum operator_sum::operator-(const elementary_operator &other) const; +template +operator_sum operator_sum::operator*(const product_operator &other) const { + std::vector coefficients; + coefficients.reserve(this->coefficients.size()); + for (auto coeff : this->coefficients) + coefficients.push_back(other.coefficients[0] * coeff); + std::vector> terms; + terms.reserve(this->terms.size()); + for (auto term : this->terms) { + std::vector prod; + prod.reserve(term.size() + other.terms[0].size()); + for (auto op : term) + prod.push_back(op); + for (auto op : other.terms[0]) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_PRODUCT(op) \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + coefficients.push_back(op other.coefficients[0]); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + terms.push_back(other.terms[0]); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_PRODUCT(+) +SUM_ADDITION_PRODUCT(-) + +template +operator_sum operator_sum::operator*(const operator_sum &other) const { + std::vector coefficients; + coefficients.reserve(this->coefficients.size() * other.coefficients.size()); + for (auto coeff1 : this->coefficients) { + for (auto coeff2 : other.coefficients) + coefficients.push_back(coeff1 * coeff2); + } + std::vector> terms; + terms.reserve(this->terms.size() * other.terms.size()); + for (auto term1 : this->terms) { + for (auto term2 : other.terms) { + std::vector prod; + prod.reserve(term1.size() + term2.size()); + for (auto op : term1) + prod.push_back(op); + for (auto op : term2) + prod.push_back(op); + terms.push_back(std::move(prod)); + } + } + operator_sum sum; + sum.coefficients = std::move(coefficients); + sum.terms = std::move(terms); + return sum; +} + +#define SUM_ADDITION_SUM(op) \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + for (auto coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + for (auto coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto term : this->terms) \ + terms.push_back(term); \ + for (auto term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ + } + +SUM_ADDITION_SUM(+); +SUM_ADDITION_SUM(-); + +template +operator_sum operator_sum::operator*(const product_operator &other) const; +template +operator_sum operator_sum::operator+(const product_operator &other) const; +template +operator_sum operator_sum::operator-(const product_operator &other) const; +template +operator_sum operator_sum::operator*(const operator_sum &other) const; +template +operator_sum operator_sum::operator+(const operator_sum &other) const; +template +operator_sum operator_sum::operator-(const operator_sum &other) const; + // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ From 53eed4531c31a5d264a2d063f626461e8031fc97 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 29 Jan 2025 20:07:09 +0000 Subject: [PATCH 187/311] finished cleanup of arithmetics Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 1 - runtime/cudaq/dynamics/arithmetics.cpp | 216 ----------------- runtime/cudaq/dynamics/memory.cpp | 221 ------------------ runtime/cudaq/dynamics/operator_sum.cpp | 207 +++++++++++++--- runtime/cudaq/dynamics/product_operators.cpp | 56 ++++- .../dynamics/{arithmetics.h => templates.h} | 0 runtime/cudaq/operators.h | 44 ++-- 7 files changed, 239 insertions(+), 506 deletions(-) delete mode 100644 runtime/cudaq/dynamics/arithmetics.cpp delete mode 100644 runtime/cudaq/dynamics/memory.cpp rename runtime/cudaq/dynamics/{arithmetics.h => templates.h} (100%) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 45bedd7d65..adad5ac53b 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -22,7 +22,6 @@ set(CUDAQ_OPS_SRC elementary_operators.cpp product_operators.cpp operator_sum.cpp - arithmetics.cpp schedule.cpp ) diff --git a/runtime/cudaq/dynamics/arithmetics.cpp b/runtime/cudaq/dynamics/arithmetics.cpp deleted file mode 100644 index 00ec426254..0000000000 --- a/runtime/cudaq/dynamics/arithmetics.cpp +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/operators.h" -#include -#include -#include -#include -#include -#include -#include - -namespace cudaq { - -// operator sum right-hand arithmetics - -template -operator_sum& operator_sum::operator*=(double other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(double other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(double other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator*=(std::complex other) { - *this *= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator+=(std::complex other) { - *this += scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator-=(std::complex other) { - *this -= scalar_operator(other); - return *this; -} - -template -operator_sum& operator_sum::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const scalar_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const scalar_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const HandlerTy &other) { - this->coefficients.push_back(1.); - this->terms.push_back({other}); - return *this; -} - -template -operator_sum& operator_sum::operator-=(const HandlerTy &other) { - this->coefficients.push_back(-1.); - this->terms.push_back({other}); - return *this; -} - -template -operator_sum& operator_sum::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const product_operator &other) { - *this = *this + other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const product_operator &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator*=(const operator_sum &other) { - *this = *this * other; - return *this; -} - -template -operator_sum& operator_sum::operator-=(const operator_sum &other) { - *this = *this - other; - return *this; -} - -template -operator_sum& operator_sum::operator+=(const operator_sum &other) { - *this = *this + other; - return *this; -} - - -// product operator right-hand arithmetics - -template -product_operator& product_operator::operator*=(double other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator& product_operator::operator*=(std::complex other) { - *this = *this * scalar_operator(other); - return *this; -} - -template -product_operator& product_operator::operator*=(const scalar_operator &other) { - *this = *this * other; - return *this; -} - -template -product_operator& product_operator::operator*=(const HandlerTy &other) { - *this = *this * other; - return *this; -} - - -template -product_operator& product_operator::operator*=(const product_operator &other) { - *this = *this * other; - return *this; -} - - -// instantiations - -template -operator_sum& operator_sum::operator*=(double other); -template -operator_sum& operator_sum::operator+=(double other); -template -operator_sum& operator_sum::operator-=(double other); -template -operator_sum& operator_sum::operator*=(std::complex other); -template -operator_sum& operator_sum::operator+=(std::complex other); -template -operator_sum& operator_sum::operator-=(std::complex other); -template -operator_sum& operator_sum::operator*=(const scalar_operator &other); -template -operator_sum& operator_sum::operator+=(const scalar_operator &other); -template -operator_sum& operator_sum::operator-=(const scalar_operator &other); -template -operator_sum& operator_sum::operator*=(const elementary_operator &other); -template -operator_sum& operator_sum::operator+=(const elementary_operator &other); -template -operator_sum& operator_sum::operator-=(const elementary_operator &other); -template -operator_sum& operator_sum::operator*=(const product_operator &other); -template -operator_sum& operator_sum::operator+=(const product_operator &other); -template -operator_sum& operator_sum::operator-=(const product_operator &other); -template -operator_sum& operator_sum::operator*=(const operator_sum &other); -template -operator_sum& operator_sum::operator-=(const operator_sum &other); -template -operator_sum& operator_sum::operator+=(const operator_sum &other); - -template -product_operator& product_operator::operator*=(double other); -template -product_operator& product_operator::operator*=(std::complex other); -template -product_operator& product_operator::operator*=(const scalar_operator &other); -template -product_operator& product_operator::operator*=(const elementary_operator &other); -template -product_operator& product_operator::operator*=(const product_operator &other); -} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/memory.cpp b/runtime/cudaq/dynamics/memory.cpp deleted file mode 100644 index 36c713a114..0000000000 --- a/runtime/cudaq/dynamics/memory.cpp +++ /dev/null @@ -1,221 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -// FILE TO BE DELETED - THERE ARE JUST SOME NOTES/EXPERIMENTS TO CHECK HOW TO AVOID UNNECESSARY COPIES - -#include -#include -#include -#include // enable_if, conjuction - -class Foo { -public: - int i; - Foo() = default; - Foo(int i) : i(i) {} - Foo(const Foo &other) : i(other.i) { - std::cout << "copy Foo" << std::endl; - } - - Foo(Foo &&other) { - *this = std::forward(other); - } - - Foo& operator=(const Foo& other) { - std::cout << "assignment Foo" << std::endl; - // Check for self-assignment - if (this != &other) { - i = other.i; - } - return *this; - } - - Foo& operator=(Foo&& other) noexcept { - std::cout << "move Foo" << std::endl; - this->i = other.i; - return *this; - } -}; - -class Baz { -public: - Foo f; - Baz(Foo&& foo) { - std::cout << "create Baz" << std::endl; - f = std::move(foo); - } - - Baz(const Foo &foo) : f(foo) { - std::cout << "create Baz" << std::endl; - } - - Baz(const Baz& other) : f(other.f) { - std::cout << "copy Baz" << std::endl; - } -}; - -class Bar { -private: - - void aggregate(const Baz& head) { - std::cout << "got last " << head.f.i << std::endl; - items.push_back(head.f); - } - - template - void aggregate(const Baz &head, Args&& ... args) - { - std::cout << "got " << head.f.i << std::endl; - items.push_back(head.f); - aggregate(std::forward(args)...); - } - -public: - std::vector items; - Bar() = default; - - Bar(Foo&& foo) { - std::cout << "create Bar from &&" << std::endl; - items.push_back(std::forward(foo)); - } - - Bar(Bar&& other) { - items = std::move(other.items); - } - - Bar(const Foo &foo) { - items.push_back(foo); - } - - Bar(const Bar &other) : items(other.items) { - std::cout << "copy Bar" << std::endl; - } - - //Bar(std::initializer_list args) : items(args) {} - - Bar& operator=(Bar&& other) noexcept { - std::cout << "move Bar" << std::endl; - this->items = std::forward>(other.items); - return *this; - } - - Bar& operator=(const Bar& other) { - std::cout << "assignment Bar" << std::endl; - // Check for self-assignment - if (this != &other) { - items = other.items; - } - return *this; - } - - template...>::value, void>> - Bar(const Args&... args) { - items.reserve(sizeof...(Args)); - std::cout << "create Bar from Baz" << std::endl; - aggregate(args...); - std::cout << "done" << std::endl; - } -}; - -class Dummy1 { -protected: - std::vector> terms; - Dummy1() = default; -public: - Dummy1(std::vector> data) : terms(data) {} - - std::vector get(int i) { - return terms[i]; - } -}; - -class Dummy2 : public Dummy1 { -public: - Dummy2(std::vector data) : Dummy1({data}) {} - - std::string get(int i) { - return terms[0][i]; - } -}; - -class Dummy3 { -public: - std::vector> items; - Dummy3(const std::vector& ops) { - std::cout << "construct Dummy3" << std::endl; - items.push_back(ops); - } - - Dummy3(std::vector&& ops) { - std::cout << "construct Dummy3" << std::endl; - items.push_back(std::move(ops)); - } -}; - -int main() -{ - Bar bar; - { - Foo foo(5); - Bar dummy(foo); // creates 1 copy of foo - std::cout << dummy.items[0].i << std::endl; - bar = Bar(foo); // creates 1 copy to construct bar, 1 copy when assigning - } - std::cout << bar.items[0].i << std::endl; - //std::cout << foo.i << std::endl; - - Baz op1(Foo(1)); - Baz op2(Foo(2)); - - Bar bar2(op1, op2); - std::cout << bar2.items[0].i << " " << bar2.items[1].i << std::endl; - - // FIXME: AVOID FOO COPY HERE - Bar bar3(Baz(Foo(3)), Baz(Foo(4))); - std::cout << bar3.items[0].i << " " << bar3.items[1].i << std::endl; - - //Bar(1, Dummy()); - - std::vector data1 = {"op1", "op2"}; - std::vector data2 = {"op3", "op4"}; - - Dummy1 d1({data1, data2}); - Dummy2 d2(data1); - - std::cout << d2.get(0) << " " << d2.get(1) << std::endl; - std::cout << ((Dummy1)d2).get(0)[0] << " " << ((Dummy1)d2).get(0)[1] << std::endl; - - std::cout << d1.get(0)[0] << " " << d1.get(0)[1] << std::endl; - std::cout << d1.get(1)[0] << " " << d1.get(1)[1] << std::endl; - - std::vector ops = {}; - ops.reserve(2); - { - ops.push_back(Bar(Foo(9))); - Bar op(Foo(10)); - ops.push_back(op); - } - - Dummy3 d3(std::move(ops)); - std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; - //std::cout << ops[0].items[0].i << std::endl; - - { - std::cout << std::endl; - // an initializer list will always make an extra copy (by design of the initializer list) - // d3 = Dummy3({Foo(11), Foo(12)}); -> this will make an extra copy... - std::vector ops2 = {}; - ops2.reserve(2); - ops2.push_back(Foo(11)); - ops2.push_back(Foo(12)); - d3 = Dummy3(std::move(ops2)); - } - std::cout << d3.items[0][0].items[0].i << " " << d3.items[0][1].items[0].i << std::endl; - - return 0; -} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 91f5e7187a..fa9f441e38 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -209,7 +209,7 @@ template operator_sum operator_sum::operator-() const { std::vector coefficients; coefficients.reserve(this->coefficients.size()); - for (auto coeff : this->coefficients) + for (auto &coeff : this->coefficients) coefficients.push_back(-1. * coeff); operator_sum sum; sum.coefficients = std::move(coefficients); @@ -235,7 +235,7 @@ operator_sum operator_sum::operator+() operator_sum operator_sum::operator*(otherTy other) const { \ std::vector coefficients; \ coefficients.reserve(this->coefficients.size()); \ - for (auto coeff : this->coefficients) \ + for (auto &coeff : this->coefficients) \ coefficients.push_back(coeff * other); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -251,14 +251,14 @@ SUM_MULTIPLICATION(const scalar_operator &); template \ operator_sum operator_sum::operator op(otherTy other) const { \ std::vector coefficients; \ - coefficients.reserve(this->terms.size() + 1); \ - coefficients.push_back(other); \ - for (auto coeff : this->coefficients) \ - coefficients.push_back(op coeff); \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op other); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ std::vector> terms; \ terms.reserve(this->terms.size() + 1); \ terms.push_back({}); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -277,10 +277,10 @@ template operator_sum operator_sum::operator*(const HandlerTy &other) const { std::vector> terms; terms.reserve(this->terms.size()); - for (auto term : this->terms) { + for (auto &term : this->terms) { std::vector prod; prod.reserve(term.size() + 1); - for (auto op : term) + for (auto &op : term) prod.push_back(op); prod.push_back(other); terms.push_back(std::move(prod)); @@ -296,16 +296,16 @@ operator_sum operator_sum::operator*(const HandlerTy &othe operator_sum operator_sum::operator op( \ const HandlerTy &other) const { \ std::vector coefficients; \ - coefficients.reserve(this->terms.size() + 1); \ - coefficients.push_back(1.); \ - for (auto coeff : this->coefficients) \ - coefficients.push_back(op coeff); \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op 1.); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ std::vector> terms; \ terms.reserve(this->terms.size() + 1); \ std::vector newTerm; \ newTerm.push_back(other); \ terms.push_back(std::move(newTerm)); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -345,16 +345,16 @@ template operator_sum operator_sum::operator*(const product_operator &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size()); - for (auto coeff : this->coefficients) + for (auto &coeff : this->coefficients) coefficients.push_back(other.coefficients[0] * coeff); std::vector> terms; terms.reserve(this->terms.size()); - for (auto term : this->terms) { + for (auto &term : this->terms) { std::vector prod; prod.reserve(term.size() + other.terms[0].size()); - for (auto op : term) + for (auto &op : term) prod.push_back(op); - for (auto op : other.terms[0]) + for (auto &op : other.terms[0]) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -370,12 +370,12 @@ operator_sum operator_sum::operator*(const product_operato const product_operator &other) const { \ std::vector coefficients; \ coefficients.reserve(this->coefficients.size() + 1); \ - for (auto coeff : this->coefficients) \ + for (auto &coeff : this->coefficients) \ coefficients.push_back(coeff); \ coefficients.push_back(op other.coefficients[0]); \ std::vector> terms; \ terms.reserve(this->terms.size() + 1); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ terms.push_back(other.terms[0]); \ operator_sum sum; \ @@ -391,19 +391,19 @@ template operator_sum operator_sum::operator*(const operator_sum &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size() * other.coefficients.size()); - for (auto coeff1 : this->coefficients) { - for (auto coeff2 : other.coefficients) + for (auto &coeff1 : this->coefficients) { + for (auto &coeff2 : other.coefficients) coefficients.push_back(coeff1 * coeff2); } std::vector> terms; terms.reserve(this->terms.size() * other.terms.size()); - for (auto term1 : this->terms) { - for (auto term2 : other.terms) { + for (auto &term1 : this->terms) { + for (auto &term2 : other.terms) { std::vector prod; prod.reserve(term1.size() + term2.size()); - for (auto op : term1) + for (auto &op : term1) prod.push_back(op); - for (auto op : term2) + for (auto &op : term2) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -420,15 +420,15 @@ operator_sum operator_sum::operator*(const operator_sum &other) const { \ std::vector coefficients; \ coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - for (auto coeff : this->coefficients) \ + for (auto &coeff : this->coefficients) \ coefficients.push_back(coeff); \ - for (auto coeff : other.coefficients) \ + for (auto &coeff : other.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto term : this->terms) \ + for (auto &term : this->terms) \ terms.push_back(term); \ - for (auto term : other.terms) \ + for (auto &term : other.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -452,6 +452,139 @@ operator_sum operator_sum::operator+(c template operator_sum operator_sum::operator-(const operator_sum &other) const; +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum& operator_sum::operator*=(otherTy other) { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return *this; \ + } + +SUM_MULTIPLICATION_ASSIGNMENT(double); +SUM_MULTIPLICATION_ASSIGNMENT(std::complex); +SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); + +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum& operator_sum::operator op##=(otherTy other) { \ + this->coefficients.push_back(op other); \ + this->terms.push_back({}); \ + return *this; \ + } + +SUM_ADDITION_ASSIGNMENT(double, +); +SUM_ADDITION_ASSIGNMENT(double, -); +SUM_ADDITION_ASSIGNMENT(std::complex, +); +SUM_ADDITION_ASSIGNMENT(std::complex, -); +SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); +SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); + +template +operator_sum& operator_sum::operator*=(const HandlerTy &other) { + for (auto &term : this->terms) + term.push_back(other); + operator_sum sum; + return *this; +} + +#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const HandlerTy &other) { \ + coefficients.push_back(op 1.); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + this->terms.push_back(std::move(newTerm)); \ + return *this; \ + } + +SUM_ADDITION_HANDLER_ASSIGNMENT(+) +SUM_ADDITION_HANDLER_ASSIGNMENT(-) + +template +operator_sum& operator_sum::operator*=(const product_operator &other) { + for (auto &coeff : this->coefficients) + coeff *= other.coefficients[0]; + for (auto &term : this->terms) { + term.reserve(term.size() + other.terms[0].size()); + for (auto &op : other.terms[0]) + term.push_back(op); + } + return *this; +} + +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const product_operator &other) { \ + this->coefficients.push_back(op other.coefficients[0]); \ + this->terms.push_back(other.terms[0]); \ + return *this; \ + } + +SUM_ADDITION_PRODUCT_ASSIGNMENT(+) +SUM_ADDITION_PRODUCT_ASSIGNMENT(-) + +template +operator_sum& operator_sum::operator*=(const operator_sum &other) { + this->coefficients.reserve(this->coefficients.size() * other.coefficients.size()); + *this = *this * other; // we need to update all coefficients and terms anyway + return *this; +} + +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const operator_sum &other) { \ + this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + for (auto &coeff : other.coefficients) \ + this->coefficients.push_back(op coeff); \ + this->terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : other.terms) \ + this->terms.push_back(term); \ + return *this; \ + } + +SUM_ADDITION_SUM_ASSIGNMENT(+); +SUM_ADDITION_SUM_ASSIGNMENT(-); + +template +operator_sum& operator_sum::operator*=(double other); +template +operator_sum& operator_sum::operator+=(double other); +template +operator_sum& operator_sum::operator-=(double other); +template +operator_sum& operator_sum::operator*=(std::complex other); +template +operator_sum& operator_sum::operator+=(std::complex other); +template +operator_sum& operator_sum::operator-=(std::complex other); +template +operator_sum& operator_sum::operator*=(const scalar_operator &other); +template +operator_sum& operator_sum::operator+=(const scalar_operator &other); +template +operator_sum& operator_sum::operator-=(const scalar_operator &other); +template +operator_sum& operator_sum::operator*=(const elementary_operator &other); +template +operator_sum& operator_sum::operator+=(const elementary_operator &other); +template +operator_sum& operator_sum::operator-=(const elementary_operator &other); +template +operator_sum& operator_sum::operator*=(const product_operator &other); +template +operator_sum& operator_sum::operator+=(const product_operator &other); +template +operator_sum& operator_sum::operator-=(const product_operator &other); +template +operator_sum& operator_sum::operator*=(const operator_sum &other); +template +operator_sum& operator_sum::operator-=(const operator_sum &other); +template +operator_sum& operator_sum::operator+=(const operator_sum &other); + // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ @@ -460,7 +593,7 @@ operator_sum operator_sum::operator-(c const operator_sum &self) { \ std::vector coefficients; \ coefficients.reserve(self.coefficients.size()); \ - for (auto coeff : self.coefficients) \ + for (auto &coeff : self.coefficients) \ coefficients.push_back(coeff * other); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -479,12 +612,12 @@ SUM_MULTIPLICATION_REVERSE(const scalar_operator &); std::vector coefficients; \ coefficients.reserve(self.terms.size() + 1); \ coefficients.push_back(other); \ - for (auto coeff : self.coefficients) \ + for (auto &coeff : self.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(self.terms.size() + 1); \ terms.push_back({}); \ - for (auto term : self.terms) \ + for (auto &term : self.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -503,11 +636,11 @@ template operator_sum operator*(const HandlerTy &other, const operator_sum &self) { std::vector> terms; terms.reserve(self.terms.size()); - for (auto term : self.terms) { + for (auto &term : self.terms) { std::vector prod; prod.reserve(term.size() + 1); prod.push_back(other); - for (auto op : term) + for (auto &op : term) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -524,14 +657,14 @@ operator_sum operator*(const HandlerTy &other, const operator_sum coefficients; \ coefficients.reserve(self.terms.size() + 1); \ coefficients.push_back(1.); \ - for (auto coeff : self.coefficients) \ + for (auto &coeff : self.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(self.terms.size() + 1); \ std::vector newTerm; \ newTerm.push_back(other); \ terms.push_back(std::move(newTerm)); \ - for (auto term : self.terms) \ + for (auto &term : self.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index d0139a4039..44b52152d1 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -250,7 +250,7 @@ template product_operator product_operator::operator*(const HandlerTy &other) const { std::vector terms; terms.reserve(this->terms[0].size() + 1); - for (auto term : this->terms[0]) + for (auto &term : this->terms[0]) terms.push_back(term); terms.push_back(other); return product_operator(this->coefficients[0], std::move(terms)); @@ -295,9 +295,9 @@ template product_operator product_operator::operator*(const product_operator &other) const { std::vector terms; terms.reserve(this->terms[0].size() + other.terms[0].size()); - for (auto term : this->terms[0]) + for (auto &term : this->terms[0]) terms.push_back(term); - for (auto term : other.terms[0]) + for (auto &term : other.terms[0]) terms.push_back(term); return product_operator(this->coefficients[0] * other.coefficients[0], std::move(terms)); } @@ -316,16 +316,16 @@ template operator_sum product_operator::operator*(const operator_sum &other) const { std::vector coefficients; coefficients.reserve(other.coefficients.size()); - for (auto coeff : other.coefficients) + for (auto &coeff : other.coefficients) coefficients.push_back(this->coefficients[0] * coeff); std::vector> terms; terms.reserve(other.terms.size()); - for (auto term : other.terms) { + for (auto &term : other.terms) { std::vector prod; prod.reserve(this->terms[0].size() + term.size()); - for (auto op : this->terms[0]) + for (auto &op : this->terms[0]) prod.push_back(op); - for (auto op : term) + for (auto &op : term) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -342,12 +342,12 @@ operator_sum product_operator::operator*(const operator_su std::vector coefficients; \ coefficients.reserve(other.coefficients.size() + 1); \ coefficients.push_back(this->coefficients[0]); \ - for (auto coeff : other.coefficients) \ + for (auto &coeff : other.coefficients) \ coefficients.push_back(op coeff); \ std::vector> terms; \ terms.reserve(other.terms.size() + 1); \ terms.push_back(this->terms[0]); \ - for (auto term : other.terms) \ + for (auto &term : other.terms) \ terms.push_back(term); \ operator_sum sum; \ sum.coefficients = std::move(coefficients); \ @@ -371,6 +371,42 @@ operator_sum product_operator::operato template operator_sum product_operator::operator-(const operator_sum &other) const; +#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + product_operator& product_operator::operator*=(otherTy other) { \ + this->coefficients[0] *= other; \ + return *this; \ + } + +PRODUCT_MULTIPLICATION_ASSIGNMENT(double); +PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); +PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); + +template +product_operator& product_operator::operator*=(const HandlerTy &other) { + this->terms[0].push_back(other); + return *this; +} + +template +product_operator& product_operator::operator*=(const product_operator &other) { + this->coefficients[0] *= other.coefficients[0]; + this->terms[0].reserve(this->terms[0].size() + other.terms[0].size()); + this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), other.terms[0].end()); + return *this; +} + +template +product_operator& product_operator::operator*=(double other); +template +product_operator& product_operator::operator*=(std::complex other); +template +product_operator& product_operator::operator*=(const scalar_operator &other); +template +product_operator& product_operator::operator*=(const elementary_operator &other); +template +product_operator& product_operator::operator*=(const product_operator &other); + // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ @@ -403,7 +439,7 @@ product_operator operator*(const HandlerTy &other, const product_oper std::vector terms; terms.reserve(self.terms[0].size() + 1); terms.push_back(other); - for (auto term : self.terms[0]) + for (auto &term : self.terms[0]) terms.push_back(term); return product_operator(self.coefficients[0], std::move(terms)); } diff --git a/runtime/cudaq/dynamics/arithmetics.h b/runtime/cudaq/dynamics/templates.h similarity index 100% rename from runtime/cudaq/dynamics/arithmetics.h rename to runtime/cudaq/dynamics/templates.h diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index f16c88270d..254df292f2 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,7 +8,7 @@ #pragma once -#include "dynamics/arithmetics.h" +#include "dynamics/templates.h" #include "definition.h" #include "utils/tensor.h" @@ -241,36 +241,37 @@ class operator_sum { operator_sum operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - operator_sum& operator*=(double other); - operator_sum& operator+=(double other); - operator_sum& operator-=(double other); operator_sum operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - operator_sum& operator*=(std::complex other); - operator_sum& operator+=(std::complex other); - operator_sum& operator-=(std::complex other); operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - operator_sum& operator*=(const scalar_operator &other); - operator_sum& operator+=(const scalar_operator &other); - operator_sum& operator-=(const scalar_operator &other); operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum& operator*=(const HandlerTy &other); - operator_sum& operator+=(const HandlerTy &other); - operator_sum& operator-=(const HandlerTy &other); operator_sum operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - operator_sum& operator*=(const product_operator &other); - operator_sum& operator+=(const product_operator &other); - operator_sum& operator-=(const product_operator &other); operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; + + operator_sum& operator*=(double other); + operator_sum& operator+=(double other); + operator_sum& operator-=(double other); + operator_sum& operator*=(std::complex other); + operator_sum& operator+=(std::complex other); + operator_sum& operator-=(std::complex other); + operator_sum& operator*=(const scalar_operator &other); + operator_sum& operator+=(const scalar_operator &other); + operator_sum& operator-=(const scalar_operator &other); + operator_sum& operator*=(const HandlerTy &other); + operator_sum& operator+=(const HandlerTy &other); + operator_sum& operator-=(const HandlerTy &other); + operator_sum& operator*=(const product_operator &other); + operator_sum& operator+=(const product_operator &other); + operator_sum& operator-=(const product_operator &other); operator_sum& operator*=(const operator_sum &other); operator_sum& operator+=(const operator_sum &other); operator_sum& operator-=(const operator_sum &other); @@ -405,27 +406,28 @@ class product_operator : public operator_sum { product_operator operator*(double other) const; operator_sum operator+(double other) const; operator_sum operator-(double other) const; - product_operator& operator*=(double other); product_operator operator*(std::complex other) const; operator_sum operator+(std::complex other) const; operator_sum operator-(std::complex other) const; - product_operator& operator*=(std::complex other); product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - product_operator& operator*=(const scalar_operator &other); product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator& operator*=(const HandlerTy &other); product_operator operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; - product_operator& operator*=(const product_operator &other); operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; + product_operator& operator*=(double other); + product_operator& operator*=(std::complex other); + product_operator& operator*=(const scalar_operator &other); + product_operator& operator*=(const HandlerTy &other); + product_operator& operator*=(const product_operator &other); + // left-hand arithmetics // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. From 97198fca09d93f56a23d8990181c22c5d8812f1e Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 14:51:54 +0000 Subject: [PATCH 188/311] picking up Anthony's changes Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 2 + runtime/cudaq/dynamics/CMakeLists.txt | 2 + .../cudaq/dynamics/elementary_operators.cpp | 78 +- runtime/cudaq/dynamics/helpers.cpp | 225 ++--- runtime/cudaq/dynamics/manipulation.cpp | 107 +++ runtime/cudaq/dynamics/operator_sum.cpp | 136 ++- runtime/cudaq/dynamics/product_operators.cpp | 105 ++- runtime/cudaq/dynamics/scalar_operators.cpp | 1 - runtime/cudaq/operators.h | 110 ++- runtime/cudaq/utils/tensor.cpp | 54 +- runtime/cudaq/utils/tensor.h | 9 + .../dynamics/elementary_ops_arithmetic.cpp | 292 ++++--- unittests/dynamics/elementary_ops_simple.cpp | 91 +- unittests/dynamics/operator_sum.cpp | 795 +++++++++++++++-- .../dynamics/product_operators_arithmetic.cpp | 796 ++++++++++++++++-- unittests/dynamics/scalar_ops_arithmetic.cpp | 1 - unittests/dynamics/scalar_ops_simple.cpp | 1 - unittests/utils/Tensor.cpp | 29 + 18 files changed, 2354 insertions(+), 480 deletions(-) create mode 100644 runtime/cudaq/dynamics/manipulation.cpp diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index d2760841ec..444173d941 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -73,6 +73,8 @@ class CallbackFunction { return *this; } + bool operator!() { return (!_callback_func); } + matrix_2 operator()(std::map degrees, std::map> parameters) const { diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index adad5ac53b..a595c9bb2b 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -23,6 +23,8 @@ set(CUDAQ_OPS_SRC product_operators.cpp operator_sum.cpp schedule.cpp + manipulation.cpp + helpers.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/elementary_operators.cpp index 8e5f42b7e3..ad663b44d4 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/elementary_operators.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" #include @@ -187,49 +186,64 @@ product_operator elementary_operator::parity(int degree) { return product_operator(1., op); } -product_operator -elementary_operator::displace(int degree, std::complex amplitude) { +product_operator elementary_operator::displace(int degree) { std::string op_id = "displace"; auto op = elementary_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; - // if (op.m_ops.find(op_id) == op.m_ops.end()) { - // auto func = [&, degree](std::map dimensions, - // std::map> _none) { - // std::size_t dimension = dimensions[degree]; - // auto temp_mat = matrix_2(dimension, dimension); - // // // displace = exp[ (amplitude * create) - (conj(amplitude) * - // annihilate) ] - // // for (std::size_t i = 0; i + 1 < dimension; i++) { - // // temp_mat[{i + 1, i}] = - // // amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - // // temp_mat[{i, i + 1}] = - // // -1. * std::conj(amplitude) * std::sqrt(static_cast(i + - // 1)) + - // // 0.0 * 'j'; - // // } - // // Not ideal that our method of computing the matrix exponential - // // requires copies here. Maybe we can just use eigen directly here - // // to limit to one copy, but we can address that later. - // auto mat = temp_mat.exp(); - // return mat; - // }; - // op.define(op_id, op.expected_dimensions, func); - // } - throw std::runtime_error("currently have a bug in implementation."); + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&, degree](std::map dimensions, + std::map> parameters) { + std::size_t dimension = dimensions[degree]; + auto displacement_amplitude = parameters["displacement"]; + auto create = matrix_2(dimension, dimension); + auto annihilate = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + create[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + annihilate[{i, i + 1}] = + std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + auto term1 = displacement_amplitude * create; + auto term2 = std::conj(displacement_amplitude) * annihilate; + return (term1 - term2).exponential(); + }; + op.define(op_id, op.expected_dimensions, func); + } return product_operator(1., op); } -product_operator -elementary_operator::squeeze(int degree, std::complex amplitude) { - throw std::runtime_error("Not yet implemented."); + +product_operator elementary_operator::squeeze(int degree) { + std::string op_id = "squeeze"; + auto op = elementary_operator(op_id, {degree}); + // A dimension of -1 indicates this operator can act on any dimension. + op.expected_dimensions[degree] = -1; + if (op.m_ops.find(op_id) == op.m_ops.end()) { + auto func = [&, degree](std::map dimensions, + std::map> parameters) { + std::size_t dimension = dimensions[degree]; + auto squeezing = parameters["squeezing"]; + auto create = matrix_2(dimension, dimension); + auto annihilate = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + create[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + annihilate[{i, i + 1}] = + std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + auto term1 = std::conj(squeezing) * annihilate.power(2); + auto term2 = squeezing * create.power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); + }; + op.define(op_id, op.expected_dimensions, func); + } + return product_operator(1., op); } -// evaluations matrix_2 elementary_operator::to_matrix( std::map dimensions, - std::map> parameters) { + std::map> parameters) const { return m_ops[id].generator(dimensions, parameters); } diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index be7906a30b..47c780a988 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -6,145 +6,148 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/operators.h" +#include #include "cudaq/helpers.h" #include "cudaq/cudm_error_handling.h" #include #include #include -namespace cudaq { -// Aggregate parameters from multiple mappings. -std::map OperatorHelpers::aggregate_parameters( - const std::vector> ¶meter_mappings) { - std::map parameter_descriptions; - - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; +class _OperatorHelpers { +public: + _OperatorHelpers() = default; + + // Aggregate parameters from multiple mappings. + static std::map aggregate_parameters( + const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; + } } } - } - - return parameter_descriptions; -} -// Extract documentation for a specific parameter from docstring. -std::string OperatorHelpers::parameter_docs(const std::string ¶m_name, - const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; + return parameter_descriptions; } - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); - - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; - - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if (std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + // Extract documentation for a specific parameter from docstring. + static std::string parameter_docs(const std::string ¶m_name, + const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } + + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", + std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if (std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + } } + } catch (...) { + return ""; } - } catch (...) { + return ""; } - return ""; -} - -// Extract positional arguments and keyword-only arguments. -std::pair, std::map> -OperatorHelpers::args_from_kwargs( - const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); + // Extract positional arguments and keyword-only arguments. + static std::pair, std::map> + args_from_kwargs( + const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); + } } - } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); + } } - } - return {extracted_args, kwonly_dict}; -} - -// Generate all possible quantum states for given degrees and dimensions -std::vector -OperatorHelpers::generate_all_states(const std::vector °rees, - const std::map &dimensions) { - if (degrees.empty()) { - return {}; + return {extracted_args, kwonly_dict}; } - std::vector> states; - for (int state = 0; state < dimensions.at(degrees[0]); state++) { - states.push_back({std::to_string(state)}); - } + /// Generates all possible states for the given dimensions ordered according + /// to the sequence of degrees (ordering is relevant if dimensions differ). + static std::vector + generate_all_states(std::vector degrees, std::map dimensions) { + if (degrees.size() == 0) + return {}; + + std::vector states; + int range = dimensions[degrees[0]]; + for (auto state = 0; state < range; state++) { + states.push_back(std::to_string(state)); + } - for (size_t i = 1; i < degrees.size(); i++) { - std::vector> new_states; - for (const auto ¤t : states) { - for (int state = 0; state < dimensions.at(degrees[i]); state++) { - auto new_entry = current; - new_entry.push_back(std::to_string(state)); - new_states.push_back(new_entry); + for (auto degree = degrees.begin() + 1; degree != degrees.end(); ++degree) { + std::string term; + std::vector result; + for (auto current : states) { + for (auto state = 0; state < dimensions[degrees[*degree]]; state++) { + result.push_back(current + std::to_string(state)); + } } + states = result; } - states = new_states; - } - std::vector result; - for (const auto &state : states) { - std::ostringstream joined; - for (const auto &s : state) { - joined << s; - } - result.push_back(joined.str()); + return states; } - return result; -} - -// Permute a given eigen matrix -void OperatorHelpers::permute_matrix(Eigen::MatrixXcd &matrix, - const std::vector &permutation) { - Eigen::MatrixXcd permuted_matrix(matrix.rows(), matrix.cols()); - for (size_t i = 0; i < permutation.size(); i++) { - for (size_t j = 0; j < permutation.size(); j++) { - permuted_matrix(i, j) = matrix(permutation[i], permutation[j]); + // Permutes the given matrix according to the given permutation. + // If states is the current order of vector entries on which the given matrix + // acts, and permuted_states is the desired order of an array on which the + // permuted matrix should act, then the permutation is defined such that + // [states[i] for i in permutation] produces permuted_states. + static cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation) { + auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); + std::vector> sorted_values; + for (std::size_t permuted : permutation) { + for (std::size_t permuted_again : permutation) { + sorted_values.push_back(matrix[{permuted, permuted_again}]); + } + } + int idx = 0; + for (std::size_t row = 0; row < result.get_rows(); row++) { + for (std::size_t col = 0; col < result.get_columns(); col++) { + result[{row, col}] = sorted_values[idx]; + idx++; + } } + return result; } - matrix = permuted_matrix; -} - -// Canonicalize degrees by sorting in descending order -std::vector -OperatorHelpers::canonicalize_degrees(const std::vector °rees) { - std::vector sorted_degrees = degrees; - std::sort(sorted_degrees.rbegin(), sorted_degrees.rend()); - return sorted_degrees; -} - -} // namespace cudaq + // Returns the degrees sorted in canonical order. + static std::vector canonicalize_degrees(std::vector degrees) { + std::sort(degrees.begin(), degrees.end(), std::greater()); + return degrees; + } +}; diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp new file mode 100644 index 0000000000..b4005eb804 --- /dev/null +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/operators.h" +#include "helpers.cpp" + +namespace cudaq { + +std::vector +MatrixArithmetics::_compute_permutation(std::vector op_degrees, + std::vector canon_degrees) { + auto states = + _OperatorHelpers::generate_all_states(canon_degrees, m_dimensions); + + std::vector reordering; + for (auto degree : op_degrees) + reordering.push_back(canon_degrees[degree]); + + std::vector result; + for (auto state : states) { + int index; + std::string term; + for (auto i : reordering) { + term += state[i]; + } + auto it = std::find(states.begin(), states.end(), term); + if (it != states.end()) + index = std::distance(states.begin(), it); + result.push_back(index); + } + + return result; +} + +// Given a matrix representation that acts on the given degrees or freedom, +// sorts the degrees and permutes the matrix to match that canonical order. +// Returns: +// A tuple consisting of the permuted matrix as well as the sequence of +// degrees of freedom in canonical order. +std::tuple> +MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, + std::vector op_degrees) { + auto canon_degrees = _OperatorHelpers::canonicalize_degrees(op_degrees); + if (op_degrees == canon_degrees) + return std::tuple>{op_matrix, canon_degrees}; + + auto permutation = this->_compute_permutation(op_degrees, canon_degrees); + auto result = _OperatorHelpers::permute_matrix(op_matrix, permutation); + return std::tuple>{result, canon_degrees}; +} + +EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, + EvaluatedMatrix op2) { + /// FIXME: do this check: + // assert len(frozenset(op1.degrees).intersection(op2.degrees)) == 0, \ + // "Operators should not have common degrees of freedom." + + auto op_degrees = op1.m_degrees; + std::copy(op2.m_degrees.begin(), op2.m_degrees.end(), + back_inserter(op_degrees)); + auto op_matrix = cudaq::kronecker(op1.m_matrix, op2.m_matrix); + auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); + return EvaluatedMatrix(new_degrees, new_matrix); +} + +EvaluatedMatrix MatrixArithmetics::mul(EvaluatedMatrix op1, + EvaluatedMatrix op2) { + // Elementary operators have sorted degrees such that we have a unique + // convention for how to define the matrix. Tensor products permute the + // computed matrix if necessary to guarantee that all operators always have + // sorted degrees. + if (op1.m_degrees != op2.m_degrees) + throw std::runtime_error( + "Operators should have the same order of degrees."); + return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix * op2.m_matrix)); +} + +EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, + EvaluatedMatrix op2) { + // Elementary operators have sorted degrees such that we have a unique + // convention for how to define the matrix. Tensor products permute the + // computed matrix if necessary to guarantee that all operators always have + // sorted degrees. + if (op1.m_degrees != op2.m_degrees) + throw std::runtime_error( + "Operators should have the same order of degrees."); + return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix + op2.m_matrix)); +} + +EvaluatedMatrix MatrixArithmetics::evaluate( + std::variant> op) { + // auto getDegrees = [](auto &&t) { return t.degrees; }; + // auto toMatrix = [&](auto &&t) { + // return t.to_matrix(this->m_dimensions, this->m_parameters); + // }; + // auto degrees = std::visit(getDegrees, op); + // auto matrix = std::visit(toMatrix, op); + // return EvaluatedMatrix(degrees, matrix); + throw std::runtime_error("implementation broken."); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index fa9f441e38..f6d63c6751 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" #include @@ -18,14 +17,124 @@ namespace cudaq { // private methods +template +cudaq::matrix_2 operator_sum::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const { + + auto terms = this->get_terms(); + + std::set degrees_set; + for (auto op : terms) { + for (auto degree : op.degrees()) { + degrees_set.insert(degree); + } + } + std::vector degrees(degrees_set.begin(), degrees_set.end()); + + // We need to make sure all matrices are of the same size to sum them up. + auto paddedTerm = [&](auto &&term) { + std::vector op_degrees; + for (auto op : term.get_terms()) { + for (auto degree : op.degrees) + op_degrees.push_back(degree); + } + for (auto degree : degrees) { + auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); + if (it == op_degrees.end()) { + term *= elementary_operator::identity(degree); + } + } + return term; + }; + + auto sum = EvaluatedMatrix(); + if (pad_terms) { + + sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, dimensions, + parameters, pad_terms)); + for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { + auto term = terms[term_idx]; + + auto eval = paddedTerm(term).m_evaluate(arithmetics, dimensions, + parameters, pad_terms); + sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + } + } else { + sum = + EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, dimensions, + parameters, pad_terms)); + for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { + auto term = terms[term_idx]; + auto eval = + term.m_evaluate(arithmetics, dimensions, parameters, pad_terms); + sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + } + } + return sum.matrix(); +} + template -std::vector> operator_sum::canonicalize_product(product_operator &prod) const { - throw std::runtime_error("not implemented"); +std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const { + std::vector scalars = {prod.get_coefficient()}; + auto non_scalars = prod.get_terms(); + + std::vector all_degrees; + for (auto op : non_scalars) { + for (auto degree : op.degrees) + all_degrees.push_back(degree); + } + + std::set unique_degrees(all_degrees.begin(), all_degrees.end()); + + if (all_degrees.size() == unique_degrees.size()) { + // Each operator acts on different degrees of freedom; they + // hence commute and can be reordered arbitrarily. + /// FIXME: Doing nothing for now + // std::sort(non_scalars.begin(), non_scalars.end(), [](auto op){ return + // op.degrees; }) + } else { + // Some degrees exist multiple times; order the scalars, identities, + // and zeros, but do not otherwise try to reorder terms. + std::vector zero_ops; + std::vector identity_ops; + std::vector non_commuting; + for (auto op : non_scalars) { + if (op.id == "zero") + zero_ops.push_back(op); + if (op.id == "identity") + identity_ops.push_back(op); + if (op.id != "zero" || op.id != "identity") + non_commuting.push_back(op); + } + + /// FIXME: Not doing the same sorting we do in python yet + std::vector sorted_non_scalars; + sorted_non_scalars.insert(sorted_non_scalars.end(), zero_ops.begin(), + zero_ops.end()); + sorted_non_scalars.insert(sorted_non_scalars.end(), identity_ops.begin(), + identity_ops.end()); + sorted_non_scalars.insert(sorted_non_scalars.end(), non_commuting.begin(), + non_commuting.end()); + non_scalars = sorted_non_scalars; + } + return std::make_tuple(scalars, non_scalars); } template -std::vector> operator_sum::_canonical_terms() const { - throw std::runtime_error("not implemented"); +std::tuple, std::vector> operator_sum::m_canonical_terms() const { + /// FIXME: Not doing the same sorting we do in python yet + std::tuple, std::vector> result; + std::vector scalars; + std::vector elementary_ops; + for (auto term : this->get_terms()) { + auto canon_term = m_canonicalize_product(term); + auto canon_scalars = std::get<0>(canon_term); + auto canon_elementary = std::get<1>(canon_term); + scalars.insert(scalars.end(), canon_scalars.begin(), canon_scalars.end()); + canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), canon_elementary.end()); + } + return std::make_tuple(scalars, elementary_ops); } template @@ -40,10 +149,15 @@ void operator_sum::aggregate_terms(const product_operator } template -std::vector> operator_sum::canonicalize_product(product_operator &prod) const; +cudaq::matrix_2 operator_sum::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const; template -std::vector> operator_sum::_canonical_terms() const; +std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; + +template +std::tuple, std::vector> operator_sum::m_canonical_terms() const; // no overload for a single product, since we don't want a constructor for a single term @@ -182,8 +296,10 @@ std::string operator_sum::to_string() const { template matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map ¶ms) const { - throw std::runtime_error("not implemented"); + const std::map> ¶meters) const { + /// FIXME: Not doing any conversion to spin op yet. + return m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, + parameters); } template @@ -191,7 +307,7 @@ std::string operator_sum::to_string() const; template matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map ¶ms) const; + const std::map> ¶ms) const; // comparisons diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 44b52152d1..2fe585324b 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" +#include "cudaq/dynamics/helpers.cpp" #include #include @@ -18,6 +18,99 @@ namespace cudaq { // private methods +cudaq::matrix_2 +_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::elementary_operator op, + std::vector degrees, std::map dimensions, + std::map> parameters) { + /// Creating the tensor product with op being last is most efficient. + std::vector padded; + for (const auto °ree : degrees) { + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == + op.degrees.end(), + degree) { + padded.push_back( + arithmetics.evaluate(cudaq::elementary_operator::identity(degree)) + .matrix()); + } + matrix_2 mat = op.to_matrix(dimensions, parameters); + padded.push_back(mat); + } + /// FIXME: This directly uses cudaq::kronecker instead of the tensor method. + /// I need to double check to make sure this gives the equivalent behavior + /// to the method used in python. + return cudaq::kronecker(padded.begin(), padded.end()); + ; +} + +template +cudaq::matrix_2 product_operator::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const { + /// Grab the underlying elementary operators. + auto terms = this->get_terms(); + + std::set noncanon_set; + for (const auto &op : terms) { + for (const auto °ree : op.degrees) { + noncanon_set.insert(degree); + } + } + std::vector noncanon_degrees(noncanon_set.begin(), noncanon_set.end()); + + // Calculate the total dimensions of the Hilbert space to create our + // identity matrix. + auto full_hilbert_size = 1; + for (const auto degree : noncanon_degrees) + full_hilbert_size *= dimensions[degree]; + cudaq::matrix_2 result(full_hilbert_size, full_hilbert_size); + // If this product operator consists only of scalar operator terms, + // we will avoid all of the below logic and just return the scalar value + // stored in an identity matrix spanning the full Hilbert space of the + // provided `dimensions`. + if (terms.size() > 0) { + if (pad_terms) { + // Sorting the degrees to avoid unnecessary permutations during the + // padding. + std::set noncanon_set; + for (const auto &op : terms) { + for (const auto °ree : op.degrees) { + noncanon_set.insert(degree); + } + } + auto degrees = _OperatorHelpers::canonicalize_degrees(noncanon_degrees); + auto evaluated = + EvaluatedMatrix(degrees, _padded_op(arithmetics, terms[0], + degrees, dimensions, parameters)); + + for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { + auto op = terms[op_idx]; + if (op.degrees.size() != 1) { + auto padded_op_to_print = + _padded_op(arithmetics, op, degrees, dimensions, parameters); + auto padded_mat = + EvaluatedMatrix(degrees, _padded_op(arithmetics, op, degrees, + dimensions, parameters)); + evaluated = arithmetics.mul(evaluated, padded_mat); + } + } + result = evaluated.matrix(); + } else { + auto evaluated = arithmetics.evaluate(terms[0]); + for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { + auto op = terms[op_idx]; + auto mat = op.to_matrix(dimensions, parameters); + evaluated = + arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + } + result = evaluated.matrix(); + } + } else { + result = cudaq::matrix_2::identity(full_hilbert_size); + } + auto coefficient = this->get_coefficient(); + return coefficient.evaluate(parameters) * result; +} + template void product_operator::aggregate_terms() {} @@ -65,6 +158,11 @@ scalar_operator product_operator::get_coefficient() const { return this->coefficients[0]; } +template +cudaq::matrix_2 product_operator::m_evaluate( + MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms) const; + template std::vector product_operator::degrees() const; @@ -180,7 +278,8 @@ template matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const { if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) - throw std::runtime_error("not implemented"); + return this->m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, + parameters); return this->get_terms()[0].to_matrix(dimensions, parameters); } @@ -236,7 +335,7 @@ PRODUCT_MULTIPLICATION(const scalar_operator &); template \ operator_sum product_operator::operator op( \ otherTy other) const { \ - return operator_sum(product_operator(other), op *this); \ + return operator_sum(product_operator(op other), *this); \ } PRODUCT_ADDITION(double, +); diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 42fac4e7e6..b933ad0783 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "common/EigenDense.h" #include "cudaq/operators.h" #include diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 254df292f2..0dc767e344 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -19,11 +19,11 @@ #include #include -// FIXME: DO WE REALLY NEED THE HANDLERTY OVERLOADS? -> EVERYTHING SHOULD BE A PRODUCT -// might be perf relevant to avoid the creation of unnecessary products... - namespace cudaq { +class MatrixArithmetics; + + class scalar_operator { private: @@ -148,11 +148,14 @@ class operator_sum { private: - std::vector> - canonicalize_product(product_operator &prod) const; + std::tuple, std::vector> + m_canonicalize_product(product_operator &prod) const; + + std::tuple, std::vector> + m_canonical_terms() const; - std::vector> - _canonical_terms() const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms = true) const; void aggregate_terms(); @@ -216,7 +219,7 @@ class operator_sum { /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. matrix_2 to_matrix(const std::map &dimensions = {}, - const std::map ¶ms = {}) const; + const std::map> ¶meters = {}) const; // comparisons @@ -319,14 +322,18 @@ class operator_sum { /// that can. template // handler needs to inherit from operation_handler class product_operator : public operator_sum { +friend class operator_sum; // FIXME: explicitly list members instead? private: void aggregate_terms(); - + template void aggregate_terms(const HandlerTy &head, Args&& ... args); + matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, + std::map> parameters, bool pad_terms = true) const; + public: // read-only properties @@ -524,9 +531,8 @@ class elementary_operator { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 - to_matrix(const std::map dimensions, - const std::map> parameters) const; + matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. @@ -543,11 +549,9 @@ class elementary_operator { static product_operator number(int degree); static product_operator parity(int degree); static product_operator position(int degree); - /// FIXME: amplitude should be a parameter that is only defined upon evaluation - static product_operator squeeze(int degree, - std::complex amplitude); - static product_operator displace(int degree, - std::complex amplitude); + /// Operators that accept parameters at runtime. + static product_operator squeeze(int degree); + static product_operator displace(int degree); /// @brief Adds the definition of an elementary operator with the given id to /// the class. After definition, an the defined elementary operator can be @@ -641,4 +645,76 @@ extern template class product_operator; extern template class operator_sum; #endif + +template +class OperatorArithmetics { +public: + /// @brief Accesses the relevant data to evaluate an operator expression + /// in the leaf nodes, that is in elementary and scalar operators. + TEval evaluate(product_operator &op); + + /// @brief Adds two operators that act on the same degrees of freedom. + TEval add(TEval val1, TEval val2); + + /// @brief Multiplies two operators that act on the same degrees of freedom. + TEval mul(TEval val1, TEval val2); + + /// @brief Computes the tensor product of two operators that act on different + /// degrees of freedom. + TEval tensor(TEval val1, TEval val2); +}; + +class EvaluatedMatrix { +friend class MatrixArithmetics; + +private: + std::vector m_degrees; + matrix_2 m_matrix; + +public: + EvaluatedMatrix() = default; + EvaluatedMatrix(std::vector degrees, matrix_2 matrix) + : m_degrees(degrees), m_matrix(matrix) {} + + /// @brief The degrees of freedom that the matrix of the evaluated value + /// applies to. + std::vector degrees() { return m_degrees; } + + /// @brief The matrix representation of an evaluated operator, according + /// to the sequence of degrees of freedom associated with the evaluated + /// value. + matrix_2 matrix() { return m_matrix; } +}; + +/// Encapsulates the functions needed to compute the matrix representation +/// of an operator expression. +class MatrixArithmetics : public OperatorArithmetics { +private: + std::map m_dimensions; + std::map> m_parameters; + std::vector _compute_permutation(std::vector op_degrees, + std::vector canon_degrees); + std::tuple> + _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); + +public: + MatrixArithmetics(std::map dimensions, + std::map> parameters) + : m_dimensions(dimensions), m_parameters(parameters) {} + + // Computes the tensor product of two evaluate operators that act on + // different degrees of freedom using the kronecker product. + EvaluatedMatrix tensor(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Multiplies two evaluated operators that act on the same degrees + // of freedom. + EvaluatedMatrix mul(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Adds two evaluated operators that act on the same degrees + // of freedom. + EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Computes the matrix of an ElementaryOperator or ScalarOperator using its + // `to_matrix` method. + EvaluatedMatrix + evaluate(std::variant> op); +}; + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index 6d4aaa9764..ce94678e57 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include "cudaq/utils/tensor.h" -#include +#include #include inline std::complex &access(std::complex *p, @@ -128,3 +128,55 @@ std::string cudaq::matrix_2::dump() const { out << '}'; return out.str(); } + +double _factorial(std::size_t value) { + if (value <= 1) + return 1; + return value * std::tgamma(value); +} + +// Calculate the power of a given matrix, `powers` times. +cudaq::matrix_2 cudaq::matrix_2::power(int powers) { + // Initialize as identity. + std::size_t rows = get_rows(); + std::size_t columns = get_columns(); + if (rows != columns) + throw std::runtime_error("Matrix power expects a square matrix."); + auto result = cudaq::matrix_2(rows, columns); + for (std::size_t i = 0; i < rows; i++) { + result[{i, i}] = 1.0 + 0.0j; + } + + // Calculate the matrix power iteratively. + for (std::size_t i = 0; i < powers; i++) { + result = result * *this; + } + return result; +} + +// Calculate the Taylor approximation to the exponential of the given matrix. +cudaq::matrix_2 cudaq::matrix_2::exponential() { + std::size_t rows = get_rows(); + std::size_t columns = get_columns(); + if (rows != columns) + throw std::runtime_error("Matrix exponential expects a square matrix."); + auto result = cudaq::matrix_2(rows, columns); + // Taylor Series Approximation, fixed at 20 steps. + std::size_t taylor_steps = 20; + for (std::size_t step = 0; step < taylor_steps; step++) { + auto term = this->power(step); + for (std::size_t i = 0; i < rows; i++) { + for (std::size_t j = 0; j < columns; j++) { + result[{i, j}] += term[{i, j}] / _factorial(step); + } + } + } + return result; +} + +cudaq::matrix_2 cudaq::matrix_2::identity(const std::size_t rows) { + auto result = cudaq::matrix_2(rows, rows); + for (std::size_t i = 0; i < rows; i++) + result[{i, i}] = 1. + 0.0j; + return result; +} diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 8287ab93de..18486385fa 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -99,6 +99,15 @@ class matrix_2 { friend matrix_2 kronecker(const matrix_2 &, const matrix_2 &); matrix_2 &kronecker_inplace(const matrix_2 &); + /// Matrix exponential, uses 20 terms of Taylor Series approximation. + matrix_2 exponential(); + + /// Matrix power. + matrix_2 power(int powers); + + /// Return a square identity matrix for the given size. + static matrix_2 identity(const std::size_t rows); + /// Kronecker a list of matrices. The list can be any container that has /// iterators defined. template diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/elementary_ops_arithmetic.cpp index 519f4d3a30..2fd90a57fe 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/elementary_ops_arithmetic.cpp @@ -9,12 +9,6 @@ #include "cudaq/operators.h" #include -/// STATUS: -/// 1. I've generated all of the `want` matrices for each test, and prepared -/// the test to check against the `got` matrix. Now waiting on finishing the -/// full `to_matrix` conversion to be able to do so. -/// - namespace utils_0 { void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_rank(), b.get_rank()); @@ -90,18 +84,21 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} void assert_product_equal(const cudaq::product_operator &got, const std::complex &expected_coefficient, @@ -115,6 +112,75 @@ void assert_product_equal(const cudaq::product_operator NOT TESTED ANYWHERE +/// We get an error in the test body when evaluating +/// the return of this function, since the `-1.0` value +/// is going out of scope somewhere down the line in its +/// conversion behind the scenes to a scalar operator. +cudaq::scalar_operator negate(cudaq::scalar_operator op) { + return -1.0 * op; +} + +TEST(OperatorExpressions, checkElementaryAgainstDouble) { + std::complex value = 0.125 + 0.125j; + + // `elementary_operator` + `complex` and `complex` + + // `elementary_operator` + { + auto elementary = cudaq::elementary_operator::annihilate(0); + + auto sum = value + elementary; + auto reverse = elementary + value; + + auto got_matrix = sum.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils_0::id_matrix(3); + auto want_matrix = scaled_identity + utils_0::annihilate_matrix(3); + auto want_matrix_reverse = utils_0::annihilate_matrix(3) + scaled_identity; + + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `elementary_operator` - `complex` and `complex` - `elementary_operator` + { + auto elementary = cudaq::elementary_operator::position(0); + + auto difference = value - elementary; + auto reverse = elementary - value; + + auto got_matrix = difference.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils_0::id_matrix(3); + auto want_matrix = scaled_identity - utils_0::position_matrix(3); + auto want_matrix_reverse = utils_0::position_matrix(3) - scaled_identity; + + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `elementary_operator` * `complex` and `complex` * + // `elementary_operator` + { + auto elementary = cudaq::elementary_operator::number(0); + + auto product = value * elementary; + auto reverse = elementary * value; + + auto got_matrix = product.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils_0::id_matrix(3); + auto want_matrix = scaled_identity * utils_0::number_matrix(3); + auto want_matrix_reverse = utils_0::number_matrix(3) * scaled_identity; + + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto function = [](std::map> parameters) { @@ -131,35 +197,30 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::scalar_operator(const_scale_factor); - // Produces an `operator_sum` type. auto sum = self + other; auto reverse = other + self; - // Check the `operator_sum` attributes. ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}, {}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::annihilate_matrix(level_count); // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator + scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::parity(0); auto other = cudaq::scalar_operator(function); - // Produces an `operator_sum` type. auto sum = self + other; auto reverse = other + self; @@ -167,24 +228,22 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) + scaled_identity; + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + // {{"value", const_scale_factor}}); + // auto got_reverse_matrix = reverse.to_matrix( + // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = - scaled_identity + utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity + utils_0::parity_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator - scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::number(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `operator_sum` type. @@ -195,26 +254,27 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}, {}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) - scaled_identity; + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + // auto got_reverse_matrix = reverse.to_matrix({{degree_index, + // level_count}}); + // auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; auto want_reverse_matrix = - scaled_identity - utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity - utils_0::number_matrix(level_count); + // std::cout << "\nwant = \n" << want_matrix.dump() << "\n"; + // std::cout << "\ngot = \n" << got_matrix.dump() << "\n"; + // std::cout << "\nwant reverse = \n" << want_reverse_matrix.dump() << "\n"; + // std::cout << "\ngot reverse = \n" << got_reverse_matrix.dump() << "\n"; + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator - scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::position(0); auto other = cudaq::scalar_operator(function); - // Produces an `operator_sum` type. auto sum = self - other; auto reverse = other - self; @@ -222,74 +282,76 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) + scaled_identity; + // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + // {{"value", const_scale_factor}}); + // auto got_reverse_matrix = + // reverse.to_matrix({{degree_index, level_count}}, {{"value", + // const_scale_factor}}); auto want_matrix = + // utils_0::position_matrix(level_count) + scaled_identity; auto want_reverse_matrix = - scaled_identity + utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity + utils_0::position_matrix(level_count); + // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator * scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); + + std::vector want_degrees = {0}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = product.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}, {}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) * scaled_identity; + auto got_matrix = product.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = utils_0::momentum_matrix(level_count) * scaled_identity; auto want_reverse_matrix = - scaled_identity * utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + scaled_identity * utils_0::momentum_matrix(level_count); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `elementary_operator * scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::elementary_operator::create(0); auto other = cudaq::scalar_operator(function); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("annihilate", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("create", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("create", {0})}); + + std::vector want_degrees = {0}; + // ASSERT_TRUE(product.degrees() == want_degrees); + // ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); // auto got_matrix = product.to_matrix({{degree_index, level_count}}, - // {"value", const_scale_factor}); auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {"value", - // const_scale_factor}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) * scaled_identity; + // {{"value", const_scale_factor}}); + // auto got_reverse_matrix = reverse.to_matrix( + // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = - scaled_identity * utils_0::annihilate_matrix(level_count); + scaled_identity * utils_0::create_matrix(level_count); // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverese_matrix); + // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } } @@ -309,12 +371,11 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Addition, different DOF's. @@ -322,21 +383,18 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(1); - // Produces an `operator_sum` type. auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + // auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full + create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, same DOF. @@ -344,17 +402,16 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(0); - // Produces an `operator_sum` type. auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + // auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) - utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, different DOF's. @@ -362,21 +419,19 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(1); - // Produces an `operator_sum` type. auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}, {}); + // auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full - create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, same DOF. @@ -384,17 +439,18 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::elementary_operator::annihilate(0); auto other = cudaq::elementary_operator::create(0); - // Produces an `product_operator` type. auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. + std::vector want_degrees = {0}; + // ASSERT_TRUE(product.degrees() == want_degrees); - // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + // /// Check the matrices. + + // auto got_matrix = product.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) * utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + // utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, different DOF's. @@ -406,6 +462,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); + std::vector want_degrees = {0, 1}; + // ASSERT_TRUE(product.degrees() == want_degrees); + /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -426,6 +485,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// Keeping this fixed throughout. int level_count = 3; + std::complex value = 0.125 + 0.5j; /// `elementary_operator + operator_sum` and `operator_sum + /// elementary_operator` @@ -442,7 +502,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(reverse.n_terms() == 3); /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); @@ -451,9 +510,9 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + // auto got_reverse_matrix = + // reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; // utils_0::checkEqual(want_matrix, got_matrix); @@ -498,7 +557,7 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { { auto self = cudaq::elementary_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + + auto operator_sum = cudaq::elementary_operator::squeeze(0) + cudaq::elementary_operator::identity(1); auto got = self * operator_sum; @@ -506,10 +565,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(got.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - for (auto &term : got.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - for (auto &term : reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); @@ -518,15 +575,16 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); + auto term_0_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::squeeze_matrix(level_count, value)); auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + // {{"squeezing", value}}); auto got_reverse_matrix = reverse.to_matrix({{0, + // level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; // utils_0::checkEqual(want_matrix, got_matrix); @@ -537,22 +595,23 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { { auto operator_sum = cudaq::elementary_operator::create(0) + cudaq::elementary_operator::identity(1); - operator_sum += cudaq::elementary_operator::annihilate(0); + operator_sum += cudaq::elementary_operator::displace(0); ASSERT_TRUE(operator_sum.n_terms() == 3); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); + auto self_full = + cudaq::kronecker(utils_0::id_matrix(level_count), + utils_0::displace_matrix(level_count, value)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::create_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + // level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; // utils_0::checkEqual(want_matrix, got_matrix); } @@ -591,7 +650,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { operator_sum *= self; ASSERT_TRUE(operator_sum.n_terms() == 2); - for (auto &term : operator_sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/elementary_ops_simple.cpp index 3b12805d3a..da8d3ace66 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/elementary_ops_simple.cpp @@ -84,18 +84,21 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} } // namespace utils @@ -109,7 +112,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto id = cudaq::elementary_operator::identity(degree_index); - auto got_id = id.to_matrix({{degree_index, level_count}}, {}); + auto got_id = id.to_matrix({{degree_index, level_count}}); auto want_id = utils::id_matrix(level_count); utils::checkEqual(want_id, got_id); } @@ -119,7 +122,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto zero = cudaq::elementary_operator::zero(degree_index); - auto got_zero = zero.to_matrix({{degree_index, level_count}}, {}); + auto got_zero = zero.to_matrix({{degree_index, level_count}}); auto want_zero = utils::zero_matrix(level_count); utils::checkEqual(want_zero, got_zero); } @@ -129,8 +132,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto annihilate = cudaq::elementary_operator::annihilate(degree_index); - auto got_annihilate = - annihilate.to_matrix({{degree_index, level_count}}, {}); + auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); auto want_annihilate = utils::annihilate_matrix(level_count); utils::checkEqual(want_annihilate, got_annihilate); } @@ -140,7 +142,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto create = cudaq::elementary_operator::create(degree_index); - auto got_create = create.to_matrix({{degree_index, level_count}}, {}); + auto got_create = create.to_matrix({{degree_index, level_count}}); auto want_create = utils::create_matrix(level_count); utils::checkEqual(want_create, got_create); } @@ -150,7 +152,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto position = cudaq::elementary_operator::position(degree_index); - auto got_position = position.to_matrix({{degree_index, level_count}}, {}); + auto got_position = position.to_matrix({{degree_index, level_count}}); auto want_position = utils::position_matrix(level_count); utils::checkEqual(want_position, got_position); } @@ -160,7 +162,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto momentum = cudaq::elementary_operator::momentum(degree_index); - auto got_momentum = momentum.to_matrix({{degree_index, level_count}}, {}); + auto got_momentum = momentum.to_matrix({{degree_index, level_count}}); auto want_momentum = utils::momentum_matrix(level_count); utils::checkEqual(want_momentum, got_momentum); } @@ -170,7 +172,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto number = cudaq::elementary_operator::number(degree_index); - auto got_number = number.to_matrix({{degree_index, level_count}}, {}); + auto got_number = number.to_matrix({{degree_index, level_count}}); auto want_number = utils::number_matrix(level_count); utils::checkEqual(want_number, got_number); } @@ -180,28 +182,41 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto parity = cudaq::elementary_operator::parity(degree_index); - auto got_parity = parity.to_matrix({{degree_index, level_count}}, {}); + auto got_parity = parity.to_matrix({{degree_index, level_count}}); auto want_parity = utils::parity_matrix(level_count); utils::checkEqual(want_parity, got_parity); } } - // // // Displacement operator. - // // { - // // for (auto level_count : levels) { - // // auto amplitude = 1.0 + 1.0j; - // // auto displace = cudaq::elementary_operator::displace(degree_index, - // // amplitude); auto got_displace = - // utils::displace.to_matrix({{degree_index, - // // level_count}}, {}); auto want_displace = - // displace_matrix(level_count, - // // amplitude); utils::checkEqual(want_displace, got_displace); - // // } - // // } - - // TODO: Squeeze operator. + // Displacement operator. + { + for (auto level_count : levels) { + auto displacement = 2.0 + 1.0j; + auto displace = cudaq::elementary_operator::displace(degree_index); + auto got_displace = displace.to_matrix({{degree_index, level_count}}, + {{"displacement", displacement}}); + auto want_displace = utils::displace_matrix(level_count, displacement); + utils::checkEqual(want_displace, got_displace); + } + } + + // Squeeze operator. + { + for (auto level_count : levels) { + auto squeezing = 2.0 + 1.0j; + auto squeeze = cudaq::elementary_operator::squeeze(degree_index); + auto got_squeeze = squeeze.to_matrix({{degree_index, level_count}}, + {{"squeezing", squeezing}}); + auto want_squeeze = utils::squeeze_matrix(level_count, squeezing); + utils::checkEqual(want_squeeze, got_squeeze); + } + } } -// TEST(OperatorExpressions, checkCustomElementaryOps) { -// // pass -// } \ No newline at end of file +//TEST(OperatorExpressions, checkCustomElementaryOps) { + // pass + + // ex: + // operator acts upon {0,2} + // user gives us dimensions for {0,1,2} +//} diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 282ddd46d6..eedc8fbd94 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include @@ -85,18 +84,21 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} } // namespace utils_2 @@ -228,39 +230,84 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { // } TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { + int level_count = 3; + std::complex value = 0.2 + 0.2j; // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto product = sum * cudaq::scalar_operator(0.1); - auto reverse = cudaq::scalar_operator(0.1) * sum; + auto product = sum * cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) * sum; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + /// Check the matrices. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = + // product.to_matrix({{1, level_count}, {2, level_count + 1}}); + // auto got_matrix_reverse = + // reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + //utils_2::checkEqual(want_matrix, got_matrix); + //utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` { + level_count = 2; auto original = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum = original + cudaq::scalar_operator(1.0); - auto reverse = cudaq::scalar_operator(1.0) + original; + auto sum = original + cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) + original; ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, + // {2,level_count+1}}); + + // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + // utils_2::create_matrix(level_count)); + // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + // utils_2::id_matrix(level_count)); + // auto sum_matrix = matrix0 + matrix1; + // auto scaled_identity = + // value * utils_2::id_matrix((level_count) * (level_count + 1)); + + // auto want_matrix = sum_matrix + scaled_identity; + // auto want_matrix_reverse = scaled_identity + sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -268,110 +315,288 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto original = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto difference = original - cudaq::scalar_operator(1.0); - auto reverse = cudaq::scalar_operator(1.0) - original; + auto difference = original - cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) - original; ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = difference.to_matrix({{1, level_count}, {2, + // level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, + // level_count}, {2, level_count+1}}); + + // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + // utils_2::create_matrix(level_count)); + // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + // utils_2::id_matrix(level_count)); + // auto sum_matrix = matrix0 + matrix1; + // auto scaled_identity = + // value * utils_2::id_matrix((level_count) * (level_count + 1)); + + // auto want_matrix = sum_matrix - scaled_identity; + // auto want_matrix_reverse = scaled_identity - sum_matrix; + // // utils_2::checkEqual(want_matrix, got_matrix); + // // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= scalar_operator` { auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::momentum(2); - sum *= cudaq::scalar_operator(0.1); + sum *= cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(0.1)); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. + // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, + // {2, + // // level_count+1}}); + + // std::vector matrices_1 = { + // utils_2::id_matrix(level_count + 1), + // utils_2::create_matrix(level_count), + // utils_2::id_matrix(level_count)}; + // std::vector matrices_2 = { + // utils_2::momentum_matrix(level_count + 1), + // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; + // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + // auto scaled_identity = + // value * + // utils_2::id_matrix((level_count + 1) * level_count * level_count); + + // auto want_matrix = (matrix0 + matrix1) * scaled_identity; + // // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += scalar_operator` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::parity(1) + + cudaq::elementary_operator::position(2); - sum += cudaq::scalar_operator(1.0); + sum += cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. + // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, + // {2, + // // level_count+1}}); + + // std::vector matrices_1 = { + // utils_2::id_matrix(level_count + 1), + // utils_2::parity_matrix(level_count), + // utils_2::id_matrix(level_count)}; + // std::vector matrices_2 = { + // utils_2::position_matrix(level_count + 1), + // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; + // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + // auto scaled_identity = + // value * + // utils_2::id_matrix((level_count + 1) * level_count * level_count); + + // auto want_matrix = matrix0 + matrix1 + scaled_identity; + // // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= scalar_operator` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::number(1) + + cudaq::elementary_operator::annihilate(2); - sum -= cudaq::scalar_operator(1.0); + sum -= cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. + // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, + // {2, + // // level_count+1}}); + + // std::vector matrices_1 = { + // utils_2::id_matrix(level_count + 1), + // utils_2::number_matrix(level_count), + // utils_2::id_matrix(level_count)}; + // std::vector matrices_2 = { + // utils_2::annihilate_matrix(level_count + 1), + // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; + // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + // auto scaled_identity = + // value * + // utils_2::id_matrix((level_count + 1) * level_count * level_count); + + // auto want_matrix = (matrix0 + matrix1) - scaled_identity; + // // utils_2::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { - std::complex value = 0.1 + 0.1; + int level_count = 3; + std::complex value = 0.1 + 0.1j; + double double_value = 0.1; // `operator_sum * double` and `double * operator_sum` { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto product = sum * 2.0; - auto reverse = 2.0 * sum; + auto product = sum * double_value; + auto reverse = double_value * sum; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = product.to_matrix({{1, level_count}, {2, + // level_count+1}}, + // {}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + double` and `double + operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::elementary_operator::momentum(1) + + cudaq::elementary_operator::position(2); - auto sum = original + 2.0; - auto reverse = 2.0 + original; + auto sum = original + double_value; + auto reverse = double_value + original; ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::momentum_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::position_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix_reverse = scaled_identity + sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - double` and `double - operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::elementary_operator::parity(1) + + cudaq::elementary_operator::number(2); - auto difference = original - 2.0; - auto reverse = 2.0 - original; + auto difference = original - double_value; + auto reverse = double_value - original; ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::number_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= double` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::squeeze(1) + + cudaq::elementary_operator::squeeze(2); - sum *= 2.0; + sum *= double_value; ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - std::cout << "GOT: " << term.get_coefficient().evaluate() << std::endl; - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {{"squeezing", value}}); + + auto matrix0 = + cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::squeeze_matrix(level_count, value)); + auto matrix1 = + cudaq::kronecker(utils_2::squeeze_matrix(level_count + 1, value), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += double` @@ -379,9 +604,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - sum += 2.0; + sum += double_value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= double` @@ -389,9 +632,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - sum -= 2.0; + sum -= double_value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * std::complex` and `std::complex * @@ -415,6 +676,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = product.to_matrix({{1,level_count}, {2, + // level_count+1}}); auto got_matrix_reverse = + // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + std::complex` and `std::complex + @@ -428,6 +710,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + // auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, + // level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix_reverse = scaled_identity + sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - std::complex` and `std::complex - @@ -441,12 +744,33 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = difference.to_matrix({{1,level_count}, {2, + // level_count+1}}); auto got_matrix_reverse = + // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= std::complex` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::displace(1) + + cudaq::elementary_operator::parity(2); sum *= value; @@ -455,84 +779,326 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {{"displacement", value}}); + + auto matrix0 = + cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::displace_matrix(level_count, value)); + auto matrix1 = cudaq::kronecker(utils_2::parity_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += std::complex` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::momentum(1) + + cudaq::elementary_operator::squeeze(2); sum += value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {{"squeezing", value}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::momentum_matrix(level_count)); + auto matrix1 = + cudaq::kronecker(utils_2::squeeze_matrix(level_count + 1, value), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= std::complex` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::elementary_operator::position(1) + + cudaq::elementary_operator::number(2); sum -= value; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // Only providing dimensions for the `1` and `2` degrees of freedom. + // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, + // {}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::position_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::number_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + // utils_2::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { + int level_count = 2; + // `operator_sum + operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + cudaq::elementary_operator::create(3); auto sum = sum_0 + sum_1; ASSERT_TRUE(sum.n_terms() == 5); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, + // level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::create_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix + sum_1_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum - operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + cudaq::elementary_operator::position(2); + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::momentum(3); auto difference = sum_0 - sum_1; ASSERT_TRUE(difference.n_terms() == 5); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = difference.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::position_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::momentum_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix - sum_1_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * operator_sum` { auto sum_0 = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::create(3); auto sum_product = sum_0 * sum_1; + auto sum_product_reverse = sum_1 * sum_0; ASSERT_TRUE(sum_product.n_terms() == 6); + ASSERT_TRUE(sum_product_reverse.n_terms() == 6); for (auto term : sum_product.get_terms()) ASSERT_TRUE(term.n_terms() == 2); + for (auto term : sum_product_reverse.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); auto + // got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::create_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix * sum_1_matrix; + auto want_matrix_reverse = sum_1_matrix * sum_0_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); + // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= operator_sum` { auto sum = cudaq::elementary_operator::create(1) + cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::identity(0) + + auto sum_1 = cudaq::elementary_operator::parity(0) + cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(2); + cudaq::elementary_operator::create(3); sum *= sum_1; ASSERT_TRUE(sum.n_terms() == 6); for (auto term : sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0,level_count}, {1, + // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + + std::vector matrices_0_0; + std::vector matrices_0_1; + std::vector matrices_1_0; + std::vector matrices_1_1; + std::vector matrices_1_2; + + matrices_0_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_0_1 = {utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_0 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + matrices_1_1 = {utils_2::id_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + matrices_1_2 = {utils_2::create_matrix(level_count + 3), + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + auto sum_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()) + + cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); + + auto want_matrix = sum_0_matrix * sum_1_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } } @@ -540,6 +1106,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { /// product operator test file. This mainly just tests the assignment operators /// between the two types. TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { + int level_count = 2; + // `operator_sum += product_operator` { auto product = cudaq::elementary_operator::annihilate(0) * @@ -550,6 +1118,38 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum += product; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, + // {2, level_count+2}}, {}); + std::vector matrices_0_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + std::vector matrices_1_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix + product_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= product_operator` @@ -562,6 +1162,38 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum -= product; ASSERT_TRUE(sum.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, + // {2, level_count+2}}); + std::vector matrices_0_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + std::vector matrices_1_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix - product_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum *= product_operator` @@ -574,9 +1206,40 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum *= product; ASSERT_TRUE(sum.n_terms() == 2); - for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 3); } + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, + // {2, level_count+2}}); + std::vector matrices_0_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), + utils_2::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_2::id_matrix(level_count + 2), + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + + std::vector matrices_1_0 = { + utils_2::id_matrix(level_count + 2), + utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix * product_matrix; + // utils_2::checkEqual(want_matrix, got_matrix); } -} +} \ No newline at end of file diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 9b399be7f1..2b8b3c04cb 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include @@ -87,22 +86,23 @@ cudaq::matrix_2 parity_matrix(std::size_t size) { return mat; } -// cudaq::matrix_2 displace_matrix(std::size_t size, -// std::complex amplitude) { -// auto mat = cudaq::matrix_2(size, size); -// for (std::size_t i = 0; i + 1 < size; i++) { -// mat[{i + 1, i}] = -// amplitude * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; -// mat[{i, i + 1}] = -1. * std::conj(amplitude) * (0.5 * 'j') * -// std::sqrt(static_cast(i + 1)) + -// 0.0 * 'j'; -// } -// return mat.exp(); -// } +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} -} // namespace utils_1 +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} -/// TODO: Not yet testing the output matrices coming from this arithmetic. +} // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector levels = {2, 3, 4}; @@ -116,13 +116,11 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; - // auto got_matrix = got.to_matrix({{0, level_count}}, {}); - - // auto matrix0 = _annihilate_matrix(level_count); - // auto matrix1 = _create_matrix(level_count); - // auto want_matrix = matrix0 * matrix1; - - // ASSERT_TRUE(want_matrix == got_matrix); + auto got_matrix = got.to_matrix({{0, level_count}}); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); + auto want_matrix = matrix0 * matrix1; + //utils_1::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; ASSERT_TRUE(got.degrees() == want_degrees); @@ -136,46 +134,68 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::elementary_operator::create(1); cudaq::product_operator got = op0 * op1; - // auto got_matrix = - // got.to_matrix({{0, level_count}, {1, level_count}}, {}); - cudaq::product_operator got_reverse = op1 * op0; - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); - - // auto identity = _id_matrix(level_count); - // auto matrix0 = _annihilate_matrix(level_count); - // auto matrix1 = _create_matrix(level_count); - - // auto fullHilbert0 = identity.kronecker(matrix0); - // auto fullHilbert1 = matrix1.kronecker(identity); - // auto want_matrix = fullHilbert0 * fullHilbert1; - // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - // ASSERT_TRUE(want_matrix == got_matrix); - // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); std::vector want_degrees = {0, 1}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + // /// Check the matrices. + // /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // got.to_matrix({{0, level_count}, {1, level_count}}, {}); + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); + + // auto identity = utils_1::id_matrix(level_count); + // auto matrix0 = utils_1::annihilate_matrix(level_count); + // auto matrix1 = utils_1::create_matrix(level_count); + + // auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + // auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + // auto want_matrix = fullHilbert0 * fullHilbert1; + // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } // Different degrees of freedom, non-consecutive. + // Should produce the same matrices as the above test. { for (auto level_count : levels) { auto op0 = cudaq::elementary_operator::annihilate(0); auto op1 = cudaq::elementary_operator::create(2); cudaq::product_operator got = op0 * op1; - // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, - // {}); - cudaq::product_operator got_reverse = op1 * op0; std::vector want_degrees = {0, 2}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, + // {}); + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0,level_count},{2,level_count}}, + // {}); + + auto identity = utils_1::id_matrix(level_count); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -187,14 +207,41 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::elementary_operator::create(2); cudaq::product_operator got = op0 * op1; - // auto got_matrix = - // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); - cudaq::product_operator got_reverse = op1 * op0; std::vector want_degrees = {0, 2}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); + // auto got_matrix_reverse = + // got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, + // {}); + + auto identity = utils_1::id_matrix(level_count); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); + + /// Identity pad the operators to compute the kronecker + /// product to the full hilbert space. + std::vector matrices_0; + std::vector matrices_1; + matrices_0 = {identity, identity, matrix0}; + matrices_1 = {matrix1, identity, identity}; + + auto fullHilbert0 = + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(got_matrix, want_matrix); } } } @@ -246,6 +293,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::complex value_0 = 0.1 + 0.1; + int level_count = 3; /// `product_operator + complex` { @@ -261,6 +309,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + double` @@ -277,13 +347,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + scalar_operator` { auto product_op = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); + auto scalar_op = cudaq::scalar_operator(value_0); auto sum = scalar_op + product_op; auto reverse = product_op + scalar_op; @@ -294,6 +385,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(sum.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - complex` @@ -310,6 +423,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - double` @@ -326,13 +461,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - scalar_operator` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(1.0); + auto product_op = cudaq::elementary_operator::momentum(0) * + cudaq::elementary_operator::momentum(1); + auto scalar_op = cudaq::scalar_operator(value_0); auto difference = scalar_op - product_op; auto reverse = product_op - scalar_op; @@ -343,13 +499,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(difference.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::momentum_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * complex` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - + auto product_op = cudaq::elementary_operator::number(0) * + cudaq::elementary_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -364,13 +541,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::number_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * double` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - + auto product_op = cudaq::elementary_operator::parity(0) * + cudaq::elementary_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -385,17 +583,35 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse_reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::parity_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::parity_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * scalar_operator` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - - ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + auto product_op = cudaq::elementary_operator::position(0) * + cudaq::elementary_operator::position(1); + auto scalar_op = cudaq::scalar_operator(value_0); - auto scalar_op = cudaq::scalar_operator(0.1); auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; @@ -407,12 +623,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); // ASSERT_TRUE(reverse.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::position_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator *= complex` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product = cudaq::elementary_operator::number(0) * + cudaq::elementary_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.n_terms() == 2); @@ -420,12 +658,30 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= double` { auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + cudaq::elementary_operator::create(1); product *= 2.0; ASSERT_TRUE(product.n_terms() == 2); @@ -433,26 +689,64 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= scalar_operator` { auto product = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(0.1); + auto scalar_op = cudaq::scalar_operator(value_0); + product *= scalar_op; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); - ASSERT_TRUE(scalar_op.evaluate() == std::complex(0.1)); + ASSERT_TRUE(scalar_op.evaluate() == value_0); std::vector want_degrees = {0, 1}; // ASSERT_TRUE(product.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), + utils_1::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + // utils_1::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { + int level_count = 3; + // `product_operator + product_operator` { auto term_0 = cudaq::elementary_operator::annihilate(0) * @@ -463,47 +757,199 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto sum = term_0 + term_1; ASSERT_TRUE(sum.n_terms() == 2); + + std::vector want_degrees = {0, 1, 2}; + // ASSERT_TRUE(sum.degrees() == want_degrees); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::annihilate_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix + term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` { auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + cudaq::elementary_operator::number(1); auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + cudaq::elementary_operator::momentum(2); auto difference = term_0 - term_1; ASSERT_TRUE(difference.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}, + // {2,level_count+1}}, {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::number_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::momentum_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix - term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * + auto term_0 = cudaq::elementary_operator::position(0) * cudaq::elementary_operator::annihilate(1); auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + cudaq::elementary_operator::parity(2); auto product = term_0 * term_1; ASSERT_TRUE(product.n_terms() == 4); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, + // {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::position_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::parity_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix * term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` { auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + cudaq::elementary_operator::number(1); auto term_1 = cudaq::elementary_operator::create(1) * cudaq::elementary_operator::annihilate(2); term_0 *= term_1; ASSERT_TRUE(term_0.n_terms() == 4); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, + // {}); + + // Build up each individual term, cast to the full Hilbert space of the + // system. + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils_1::id_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + matrices_0_1 = {utils_1::id_matrix(level_count + 1), + utils_1::number_matrix(level_count), + utils_1::id_matrix(level_count)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), + utils_1::id_matrix(level_count)}; + matrices_1_1 = {utils_1::annihilate_matrix(level_count + 1), + utils_1::id_matrix(level_count), + utils_1::id_matrix(level_count)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + + auto want_matrix = term_0_matrix * term_1_matrix; + // utils_1::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { + int level_count = 3; + // `product_operator + elementary_operator` { auto product = cudaq::elementary_operator::annihilate(0) * @@ -515,6 +961,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix + elementary_matrix; + auto want_matrix_reverse = elementary_matrix + product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - elementary_operator` @@ -528,6 +996,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix - elementary_matrix; + auto want_matrix_reverse = elementary_matrix - product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * elementary_operator` @@ -541,6 +1031,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix * elementary_matrix; + auto want_matrix_reverse = elementary_matrix * product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator *= elementary_operator` @@ -552,11 +1064,31 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { product *= elementary; ASSERT_TRUE(product.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count}}, {}); + + auto product_matrix = + cudaq::kronecker(utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)) * + cudaq::kronecker(utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + + auto want_matrix = product_matrix * elementary_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); } } TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { + int level_count = 3; + // `product_operator + operator_sum` { auto product = cudaq::elementary_operator::annihilate(0) * @@ -569,20 +1101,92 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, {}); + // auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); + + // Cast every term to full Hilbert space. + std::vector matrices_0_0 = { + utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_1::create_matrix(level_count + 1), + utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix + sum_matrix; + auto want_matrix_reverse = sum_matrix + product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` { auto product = cudaq::elementary_operator::annihilate(0) * cudaq::elementary_operator::annihilate(1); - auto original_sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original_difference = cudaq::elementary_operator::create(1) - + cudaq::elementary_operator::create(2); - auto difference = product - original_sum; - auto reverse = original_sum - product; + auto difference = product - original_difference; + auto reverse = original_difference - product; ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); + + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); + + // Cast every term to full Hilbert space. + std::vector matrices_0_0 = { + utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_1::create_matrix(level_count + 1), + utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto difference_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) - + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix - difference_matrix; + auto want_matrix_reverse = difference_matrix - product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -598,12 +1202,40 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 3); - } - - for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 3); - } + /// Check the matrices. + /// FIXME: Comment me back in when `to_matrix` is implemented. + + // auto got_matrix = + // product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); auto got_matrix_reverse = + // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, + // {}); + + // Cast every term to full Hilbert space. + std::vector matrices_0_0 = { + utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), + utils_1::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils_1::id_matrix(level_count + 1), + utils_1::annihilate_matrix(level_count), + utils_1::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils_1::id_matrix(level_count + 1), + utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils_1::create_matrix(level_count + 1), + utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix * sum_matrix; + auto want_matrix_reverse = sum_matrix * product_matrix; + + // utils_1::checkEqual(want_matrix, got_matrix); + // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index c3ef485228..b6d9a48518 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index 5ec80350f4..550b277f00 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/matrix.h" #include "cudaq/operators.h" #include diff --git a/unittests/utils/Tensor.cpp b/unittests/utils/Tensor.cpp index feb7b05970..e996ee957a 100644 --- a/unittests/utils/Tensor.cpp +++ b/unittests/utils/Tensor.cpp @@ -162,3 +162,32 @@ TEST(Tensor, kroneckerOnList) { "{ { (3,3) (6,6) }\n { (4,4) (8,8) }\n { (5,5) (10,10) }\n }"); } } + +TEST(Tensor, exponential) { + { + cudaq::matrix_2 me({1., 1., 0.5, 0.0}, {2, 2}); + cudaq::matrix_2 mf({1., 0., 1., .5, .7, 0., 1., 0., 2.}, {3, 3}); + cudaq::matrix_2 mg( + {1., 0., .4, .6, .7, .8, .9, 0., .3, .1, .2, 1., 0., 0.5, 0.2, .5}, + {4, 4}); + + auto me_exp = me.exponential(); + auto mf_exp = mf.exponential(); + auto mg_exp = mg.exponential(); + + EXPECT_EQ( + me_exp.dump(), + "{ { (3.23795,0) (1.86268,0) }\n { (0.93134,0) (1.37527,0) }\n }"); + + EXPECT_EQ( + mf_exp.dump(), + "{ { (4.84921,0) (0,0) (5.4755,0) }\n { (1.46673,0) (2.01375,0) " + "(0.977708,0) }\n { (5.4755,0) (0,0) (10.3247,0) }\n }"); + + EXPECT_EQ(mg_exp.dump(), + "{ { (2.9751,0) (0.447969,0) (1.01977,0) (1.75551,0) }\n { " + "(2.10247,0) (2.55646,0) (1.97654,0) (1.39927,0) }\n { " + "(0.800451,0) (0.648569,0) (1.69099,0) (1.76597,0) }\n { " + "(0.498881,0) (1.05119,0) (0.753502,0) (2.03447,0) }\n }"); + } +} From 759ac0b4bf48c1b28e0e75b09ef841dfe0e22e58 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 15:08:26 +0000 Subject: [PATCH 189/311] just a quick renaming Signed-off-by: Bettina Heim --- docs/sphinx/api/languages/cpp_api.rst | 2 +- runtime/cudaq/dynamics/CMakeLists.txt | 2 +- runtime/cudaq/dynamics/manipulation.cpp | 2 +- ...ary_operators.cpp => matrix_operators.cpp} | 64 +++---- runtime/cudaq/dynamics/operator_sum.cpp | 166 +++++++++--------- runtime/cudaq/dynamics/product_operators.cpp | 136 +++++++------- runtime/cudaq/dynamics/templates.h | 50 +++--- runtime/cudaq/operators.h | 58 +++--- unittests/CMakeLists.txt | 4 +- ...ithmetic.cpp => matrix_ops_arithmetic.cpp} | 132 +++++++------- ...y_ops_simple.cpp => matrix_ops_simple.cpp} | 20 +-- unittests/dynamics/operator_sum.cpp | 156 ++++++++-------- .../dynamics/product_operators_arithmetic.cpp | 156 ++++++++-------- 13 files changed, 474 insertions(+), 474 deletions(-) rename runtime/cudaq/dynamics/{elementary_operators.cpp => matrix_operators.cpp} (81%) rename unittests/dynamics/{elementary_ops_arithmetic.cpp => matrix_ops_arithmetic.cpp} (85%) rename unittests/dynamics/{elementary_ops_simple.cpp => matrix_ops_simple.cpp} (90%) diff --git a/docs/sphinx/api/languages/cpp_api.rst b/docs/sphinx/api/languages/cpp_api.rst index b4c12fac2b..79065bebb2 100644 --- a/docs/sphinx/api/languages/cpp_api.rst +++ b/docs/sphinx/api/languages/cpp_api.rst @@ -242,7 +242,7 @@ Dynamics .. doxygenclass:: cudaq::scalar_operator :members: -.. doxygenclass:: cudaq::elementary_operator +.. doxygenclass:: cudaq::matrix_operator :members: .. doxygenclass:: cudaq::OperatorArithmetics diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index a595c9bb2b..170c7b963d 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -19,7 +19,7 @@ set(CUDAQ_OPS_SRC runge_kutta_integrator.cpp definition.cpp scalar_operators.cpp - elementary_operators.cpp + matrix_operators.cpp product_operators.cpp operator_sum.cpp schedule.cpp diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index b4005eb804..8c245f818d 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -93,7 +93,7 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, } EvaluatedMatrix MatrixArithmetics::evaluate( - std::variant> op) { + std::variant> op) { // auto getDegrees = [](auto &&t) { return t.degrees; }; // auto toMatrix = [&](auto &&t) { // return t.to_matrix(this->m_dimensions, this->m_parameters); diff --git a/runtime/cudaq/dynamics/elementary_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp similarity index 81% rename from runtime/cudaq/dynamics/elementary_operators.cpp rename to runtime/cudaq/dynamics/matrix_operators.cpp index ad663b44d4..4e7a7a6bcb 100644 --- a/runtime/cudaq/dynamics/elementary_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -14,11 +14,11 @@ namespace cudaq { -std::map elementary_operator::m_ops = {}; +std::map matrix_operator::m_ops = {}; -product_operator elementary_operator::identity(int degree) { +product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -35,12 +35,12 @@ product_operator elementary_operator::identity(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::zero(int degree) { +product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -55,12 +55,12 @@ product_operator elementary_operator::zero(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::annihilate(int degree) { +product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -75,12 +75,12 @@ product_operator elementary_operator::annihilate(int degree }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::create(int degree) { +product_operator matrix_operator::create(int degree) { std::string op_id = "create"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -95,12 +95,12 @@ product_operator elementary_operator::create(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::position(int degree) { +product_operator matrix_operator::position(int degree) { std::string op_id = "position"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -119,12 +119,12 @@ product_operator elementary_operator::position(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::momentum(int degree) { +product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -143,12 +143,12 @@ product_operator elementary_operator::momentum(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::number(int degree) { +product_operator matrix_operator::number(int degree) { std::string op_id = "number"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -163,12 +163,12 @@ product_operator elementary_operator::number(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::parity(int degree) { +product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -183,12 +183,12 @@ product_operator elementary_operator::parity(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::displace(int degree) { +product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -209,13 +209,13 @@ product_operator elementary_operator::displace(int degree) }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -product_operator elementary_operator::squeeze(int degree) { +product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; - auto op = elementary_operator(op_id, {degree}); + auto op = matrix_operator(op_id, {degree}); // A dimension of -1 indicates this operator can act on any dimension. op.expected_dimensions[degree] = -1; if (op.m_ops.find(op_id) == op.m_ops.end()) { @@ -237,11 +237,11 @@ product_operator elementary_operator::squeeze(int degree) { }; op.define(op_id, op.expected_dimensions, func); } - return product_operator(1., op); + return product_operator(1., op); } -matrix_2 elementary_operator::to_matrix( +matrix_2 matrix_operator::to_matrix( std::map dimensions, std::map> parameters) const { return m_ops[id].generator(dimensions, parameters); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index f6d63c6751..3debd0e6c0 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -42,7 +42,7 @@ cudaq::matrix_2 operator_sum::m_evaluate( for (auto degree : degrees) { auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); if (it == op_degrees.end()) { - term *= elementary_operator::identity(degree); + term *= matrix_operator::identity(degree); } } return term; @@ -96,9 +96,9 @@ std::tuple, std::vector> operator_sum zero_ops; - std::vector identity_ops; - std::vector non_commuting; + std::vector zero_ops; + std::vector identity_ops; + std::vector non_commuting; for (auto op : non_scalars) { if (op.id == "zero") zero_ops.push_back(op); @@ -109,7 +109,7 @@ std::tuple, std::vector> operator_sum sorted_non_scalars; + std::vector sorted_non_scalars; sorted_non_scalars.insert(sorted_non_scalars.end(), zero_ops.begin(), zero_ops.end()); sorted_non_scalars.insert(sorted_non_scalars.end(), identity_ops.begin(), @@ -124,9 +124,9 @@ std::tuple, std::vector> operator_sum std::tuple, std::vector> operator_sum::m_canonical_terms() const { /// FIXME: Not doing the same sorting we do in python yet - std::tuple, std::vector> result; + std::tuple, std::vector> result; std::vector scalars; - std::vector elementary_ops; + std::vector matrix_ops; for (auto term : this->get_terms()) { auto canon_term = m_canonicalize_product(term); auto canon_scalars = std::get<0>(canon_term); @@ -134,7 +134,7 @@ std::tuple, std::vector> operator_sum @@ -149,26 +149,26 @@ void operator_sum::aggregate_terms(const product_operator } template -cudaq::matrix_2 operator_sum::m_evaluate( +cudaq::matrix_2 operator_sum::m_evaluate( MatrixArithmetics arithmetics, std::map dimensions, std::map> parameters, bool pad_terms) const; template -std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; +std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; template -std::tuple, std::vector> operator_sum::m_canonical_terms() const; +std::tuple, std::vector> operator_sum::m_canonical_terms() const; // no overload for a single product, since we don't want a constructor for a single term template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2); +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2); template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +void operator_sum::aggregate_terms(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); // read-only properties @@ -193,13 +193,13 @@ std::vector> operator_sum::get_terms() co } template -std::vector operator_sum::degrees() const; +std::vector operator_sum::degrees() const; template -int operator_sum::n_terms() const; +int operator_sum::n_terms() const; template -std::vector> operator_sum::get_terms() const; +std::vector> operator_sum::get_terms() const; // constructors @@ -241,25 +241,25 @@ operator_sum::operator_sum(operator_sum &&other) // no constructor for a single product, since that one should remain a product op template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2); +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2); template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +operator_sum::operator_sum(const product_operator &item1, + const product_operator &item2, + const product_operator &item3); template -operator_sum::operator_sum(const std::vector> &terms); +operator_sum::operator_sum(const std::vector> &terms); template -operator_sum::operator_sum(std::vector> &&terms); +operator_sum::operator_sum(std::vector> &&terms); template -operator_sum::operator_sum(const operator_sum &other); +operator_sum::operator_sum(const operator_sum &other); template -operator_sum::operator_sum(operator_sum &&other); +operator_sum::operator_sum(operator_sum &&other); // assignments @@ -282,10 +282,10 @@ operator_sum& operator_sum::operator=(operator_sum& operator_sum::operator=(const operator_sum& other); +operator_sum& operator_sum::operator=(const operator_sum& other); template -operator_sum& operator_sum::operator=(operator_sum &&other); +operator_sum& operator_sum::operator=(operator_sum &&other); // evaluations @@ -303,10 +303,10 @@ matrix_2 operator_sum::to_matrix(const std::map &dimensions } template -std::string operator_sum::to_string() const; +std::string operator_sum::to_string() const; template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, +matrix_2 operator_sum::to_matrix(const std::map &dimensions, const std::map> ¶ms) const; // comparisons @@ -317,7 +317,7 @@ bool operator_sum::operator==(const operator_sum &other) c } template -bool operator_sum::operator==(const operator_sum &other) const; +bool operator_sum::operator==(const operator_sum &other) const; // unary operators @@ -339,10 +339,10 @@ operator_sum operator_sum::operator+() const { } template -operator_sum operator_sum::operator-() const; +operator_sum operator_sum::operator-() const; template -operator_sum operator_sum::operator+() const; +operator_sum operator_sum::operator+() const; // right-hand arithmetics @@ -433,29 +433,29 @@ SUM_ADDITION_HANDLER(+) SUM_ADDITION_HANDLER(-) template -operator_sum operator_sum::operator*(double other) const; +operator_sum operator_sum::operator*(double other) const; template -operator_sum operator_sum::operator+(double other) const; +operator_sum operator_sum::operator+(double other) const; template -operator_sum operator_sum::operator-(double other) const; +operator_sum operator_sum::operator-(double other) const; template -operator_sum operator_sum::operator*(std::complex other) const; +operator_sum operator_sum::operator*(std::complex other) const; template -operator_sum operator_sum::operator+(std::complex other) const; +operator_sum operator_sum::operator+(std::complex other) const; template -operator_sum operator_sum::operator-(std::complex other) const; +operator_sum operator_sum::operator-(std::complex other) const; template -operator_sum operator_sum::operator*(const scalar_operator &other) const; +operator_sum operator_sum::operator*(const scalar_operator &other) const; template -operator_sum operator_sum::operator+(const scalar_operator &other) const; +operator_sum operator_sum::operator+(const scalar_operator &other) const; template -operator_sum operator_sum::operator-(const scalar_operator &other) const; +operator_sum operator_sum::operator-(const scalar_operator &other) const; template -operator_sum operator_sum::operator*(const elementary_operator &other) const; +operator_sum operator_sum::operator*(const matrix_operator &other) const; template -operator_sum operator_sum::operator+(const elementary_operator &other) const; +operator_sum operator_sum::operator+(const matrix_operator &other) const; template -operator_sum operator_sum::operator-(const elementary_operator &other) const; +operator_sum operator_sum::operator-(const matrix_operator &other) const; template operator_sum operator_sum::operator*(const product_operator &other) const { @@ -556,17 +556,17 @@ SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); template -operator_sum operator_sum::operator*(const product_operator &other) const; +operator_sum operator_sum::operator*(const product_operator &other) const; template -operator_sum operator_sum::operator+(const product_operator &other) const; +operator_sum operator_sum::operator+(const product_operator &other) const; template -operator_sum operator_sum::operator-(const product_operator &other) const; +operator_sum operator_sum::operator-(const product_operator &other) const; template -operator_sum operator_sum::operator*(const operator_sum &other) const; +operator_sum operator_sum::operator*(const operator_sum &other) const; template -operator_sum operator_sum::operator+(const operator_sum &other) const; +operator_sum operator_sum::operator+(const operator_sum &other) const; template -operator_sum operator_sum::operator-(const operator_sum &other) const; +operator_sum operator_sum::operator-(const operator_sum &other) const; #define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -665,41 +665,41 @@ SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); template -operator_sum& operator_sum::operator*=(double other); +operator_sum& operator_sum::operator*=(double other); template -operator_sum& operator_sum::operator+=(double other); +operator_sum& operator_sum::operator+=(double other); template -operator_sum& operator_sum::operator-=(double other); +operator_sum& operator_sum::operator-=(double other); template -operator_sum& operator_sum::operator*=(std::complex other); +operator_sum& operator_sum::operator*=(std::complex other); template -operator_sum& operator_sum::operator+=(std::complex other); +operator_sum& operator_sum::operator+=(std::complex other); template -operator_sum& operator_sum::operator-=(std::complex other); +operator_sum& operator_sum::operator-=(std::complex other); template -operator_sum& operator_sum::operator*=(const scalar_operator &other); +operator_sum& operator_sum::operator*=(const scalar_operator &other); template -operator_sum& operator_sum::operator+=(const scalar_operator &other); +operator_sum& operator_sum::operator+=(const scalar_operator &other); template -operator_sum& operator_sum::operator-=(const scalar_operator &other); +operator_sum& operator_sum::operator-=(const scalar_operator &other); template -operator_sum& operator_sum::operator*=(const elementary_operator &other); +operator_sum& operator_sum::operator*=(const matrix_operator &other); template -operator_sum& operator_sum::operator+=(const elementary_operator &other); +operator_sum& operator_sum::operator+=(const matrix_operator &other); template -operator_sum& operator_sum::operator-=(const elementary_operator &other); +operator_sum& operator_sum::operator-=(const matrix_operator &other); template -operator_sum& operator_sum::operator*=(const product_operator &other); +operator_sum& operator_sum::operator*=(const product_operator &other); template -operator_sum& operator_sum::operator+=(const product_operator &other); +operator_sum& operator_sum::operator+=(const product_operator &other); template -operator_sum& operator_sum::operator-=(const product_operator &other); +operator_sum& operator_sum::operator-=(const product_operator &other); template -operator_sum& operator_sum::operator*=(const operator_sum &other); +operator_sum& operator_sum::operator*=(const operator_sum &other); template -operator_sum& operator_sum::operator-=(const operator_sum &other); +operator_sum& operator_sum::operator-=(const operator_sum &other); template -operator_sum& operator_sum::operator+=(const operator_sum &other); +operator_sum& operator_sum::operator+=(const operator_sum &other); // left-hand arithmetics @@ -792,28 +792,28 @@ SUM_ADDITION_HANDLER_REVERSE(+) SUM_ADDITION_HANDLER_REVERSE(-) template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); +operator_sum operator*(const scalar_operator &other, const operator_sum &self); template -operator_sum operator*(std::complex other, const operator_sum &self); +operator_sum operator*(std::complex other, const operator_sum &self); template -operator_sum operator*(double other, const operator_sum &self); +operator_sum operator*(double other, const operator_sum &self); template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); +operator_sum operator*(const matrix_operator &other, const operator_sum &self); template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); +operator_sum operator+(const scalar_operator &other, const operator_sum &self); template -operator_sum operator+(double other, const operator_sum &self); +operator_sum operator+(double other, const operator_sum &self); template -operator_sum operator+(std::complex other, const operator_sum &self); +operator_sum operator+(std::complex other, const operator_sum &self); template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); +operator_sum operator+(const matrix_operator &other, const operator_sum &self); template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); +operator_sum operator-(const scalar_operator &other, const operator_sum &self); template -operator_sum operator-(double other, const operator_sum &self); +operator_sum operator-(double other, const operator_sum &self); template -operator_sum operator-(std::complex other, const operator_sum &self); +operator_sum operator-(std::complex other, const operator_sum &self); template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); +operator_sum operator-(const matrix_operator &other, const operator_sum &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 2fe585324b..87c8237f2e 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -19,7 +19,7 @@ namespace cudaq { // private methods cudaq::matrix_2 -_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::elementary_operator op, +_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::matrix_operator op, std::vector degrees, std::map dimensions, std::map> parameters) { /// Creating the tensor product with op being last is most efficient. @@ -29,7 +29,7 @@ _padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::elementary_operator op, op.degrees.end(), degree) { padded.push_back( - arithmetics.evaluate(cudaq::elementary_operator::identity(degree)) + arithmetics.evaluate(cudaq::matrix_operator::identity(degree)) .matrix()); } matrix_2 mat = op.to_matrix(dimensions, parameters); @@ -122,13 +122,13 @@ void product_operator::aggregate_terms(const HandlerTy &head, Args&& } template -void product_operator::aggregate_terms(const elementary_operator &item1, - const elementary_operator &item2); +void product_operator::aggregate_terms(const matrix_operator &item1, + const matrix_operator &item2); template -void product_operator::aggregate_terms(const elementary_operator &item1, - const elementary_operator &item2, - const elementary_operator &item3); +void product_operator::aggregate_terms(const matrix_operator &item1, + const matrix_operator &item2, + const matrix_operator &item3); // read-only properties @@ -159,21 +159,21 @@ scalar_operator product_operator::get_coefficient() const { } template -cudaq::matrix_2 product_operator::m_evaluate( +cudaq::matrix_2 product_operator::m_evaluate( MatrixArithmetics arithmetics, std::map dimensions, std::map> parameters, bool pad_terms) const; template -std::vector product_operator::degrees() const; +std::vector product_operator::degrees() const; template -int product_operator::n_terms() const; +int product_operator::n_terms() const; template -std::vector product_operator::get_terms() const; +std::vector product_operator::get_terms() const; template -scalar_operator product_operator::get_coefficient() const; +scalar_operator product_operator::get_coefficient() const; // constructors @@ -212,34 +212,34 @@ product_operator::product_operator(product_operator &&othe } template -product_operator::product_operator(scalar_operator coefficient); +product_operator::product_operator(scalar_operator coefficient); template -product_operator::product_operator(scalar_operator coefficient, - const elementary_operator &item1); +product_operator::product_operator(scalar_operator coefficient, + const matrix_operator &item1); template -product_operator::product_operator(scalar_operator coefficient, - const elementary_operator &item1, - const elementary_operator &item2); +product_operator::product_operator(scalar_operator coefficient, + const matrix_operator &item1, + const matrix_operator &item2); template -product_operator::product_operator(scalar_operator coefficient, - const elementary_operator &item1, - const elementary_operator &item2, - const elementary_operator &item3); +product_operator::product_operator(scalar_operator coefficient, + const matrix_operator &item1, + const matrix_operator &item2, + const matrix_operator &item3); template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); template -product_operator::product_operator(const product_operator &other); +product_operator::product_operator(const product_operator &other); template -product_operator::product_operator(product_operator &&other); +product_operator::product_operator(product_operator &&other); // assignments @@ -262,10 +262,10 @@ product_operator& product_operator::operator=(product_oper } template -product_operator& product_operator::operator=(const product_operator &other); +product_operator& product_operator::operator=(const product_operator &other); template -product_operator& product_operator::operator=(product_operator &&other); +product_operator& product_operator::operator=(product_operator &&other); // evaluations @@ -284,10 +284,10 @@ matrix_2 product_operator::to_matrix(std::map dimensions, } template -std::string product_operator::to_string() const; +std::string product_operator::to_string() const; template -matrix_2 product_operator::to_matrix(std::map dimensions, +matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const; // comparisons @@ -298,7 +298,7 @@ bool product_operator::operator==(const product_operator & } template -bool product_operator::operator==(const product_operator &other) const; +bool product_operator::operator==(const product_operator &other) const; // unary operators @@ -313,10 +313,10 @@ product_operator product_operator::operator+() const { } template -product_operator product_operator::operator-() const; +product_operator product_operator::operator-() const; template -product_operator product_operator::operator+() const; +product_operator product_operator::operator+() const; // right-hand arithmetics @@ -366,29 +366,29 @@ PRODUCT_ADDITION_HANDLER(+) PRODUCT_ADDITION_HANDLER(-) template -product_operator product_operator::operator*(double other) const; +product_operator product_operator::operator*(double other) const; template -operator_sum product_operator::operator+(double other) const; +operator_sum product_operator::operator+(double other) const; template -operator_sum product_operator::operator-(double other) const; +operator_sum product_operator::operator-(double other) const; template -product_operator product_operator::operator*(std::complex other) const; +product_operator product_operator::operator*(std::complex other) const; template -operator_sum product_operator::operator+(std::complex other) const; +operator_sum product_operator::operator+(std::complex other) const; template -operator_sum product_operator::operator-(std::complex other) const; +operator_sum product_operator::operator-(std::complex other) const; template -product_operator product_operator::operator*(const scalar_operator &other) const; +product_operator product_operator::operator*(const scalar_operator &other) const; template -operator_sum product_operator::operator+(const scalar_operator &other) const; +operator_sum product_operator::operator+(const scalar_operator &other) const; template -operator_sum product_operator::operator-(const scalar_operator &other) const; +operator_sum product_operator::operator-(const scalar_operator &other) const; template -product_operator product_operator::operator*(const elementary_operator &other) const; +product_operator product_operator::operator*(const matrix_operator &other) const; template -operator_sum product_operator::operator+(const elementary_operator &other) const; +operator_sum product_operator::operator+(const matrix_operator &other) const; template -operator_sum product_operator::operator-(const elementary_operator &other) const; +operator_sum product_operator::operator-(const matrix_operator &other) const; template product_operator product_operator::operator*(const product_operator &other) const { @@ -458,17 +458,17 @@ PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) template -product_operator product_operator::operator*(const product_operator &other) const; +product_operator product_operator::operator*(const product_operator &other) const; template -operator_sum product_operator::operator+(const product_operator &other) const; +operator_sum product_operator::operator+(const product_operator &other) const; template -operator_sum product_operator::operator-(const product_operator &other) const; +operator_sum product_operator::operator-(const product_operator &other) const; template -operator_sum product_operator::operator*(const operator_sum &other) const; +operator_sum product_operator::operator*(const operator_sum &other) const; template -operator_sum product_operator::operator+(const operator_sum &other) const; +operator_sum product_operator::operator+(const operator_sum &other) const; template -operator_sum product_operator::operator-(const operator_sum &other) const; +operator_sum product_operator::operator-(const operator_sum &other) const; #define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -496,15 +496,15 @@ product_operator& product_operator::operator*=(const produ } template -product_operator& product_operator::operator*=(double other); +product_operator& product_operator::operator*=(double other); template -product_operator& product_operator::operator*=(std::complex other); +product_operator& product_operator::operator*=(std::complex other); template -product_operator& product_operator::operator*=(const scalar_operator &other); +product_operator& product_operator::operator*=(const scalar_operator &other); template -product_operator& product_operator::operator*=(const elementary_operator &other); +product_operator& product_operator::operator*=(const matrix_operator &other); template -product_operator& product_operator::operator*=(const product_operator &other); +product_operator& product_operator::operator*=(const product_operator &other); // left-hand arithmetics @@ -554,28 +554,28 @@ PRODUCT_ADDITION_HANDLER_REVERSE(+) PRODUCT_ADDITION_HANDLER_REVERSE(-) template -product_operator operator*(double other, const product_operator &self); +product_operator operator*(double other, const product_operator &self); template -product_operator operator*(std::complex other, const product_operator &self); +product_operator operator*(std::complex other, const product_operator &self); template -product_operator operator*(const scalar_operator &other, const product_operator &self); +product_operator operator*(const scalar_operator &other, const product_operator &self); template -product_operator operator*(const elementary_operator &other, const product_operator &self); +product_operator operator*(const matrix_operator &other, const product_operator &self); template -operator_sum operator+(double other, const product_operator &self); +operator_sum operator+(double other, const product_operator &self); template -operator_sum operator+(std::complex other, const product_operator &self); +operator_sum operator+(std::complex other, const product_operator &self); template -operator_sum operator+(const scalar_operator &other, const product_operator &self); +operator_sum operator+(const scalar_operator &other, const product_operator &self); template -operator_sum operator+(const elementary_operator &other, const product_operator &self); +operator_sum operator+(const matrix_operator &other, const product_operator &self); template -operator_sum operator-(double other, const product_operator &self); +operator_sum operator-(double other, const product_operator &self); template -operator_sum operator-(std::complex other, const product_operator &self); +operator_sum operator-(std::complex other, const product_operator &self); template -operator_sum operator-(const scalar_operator &other, const product_operator &self); +operator_sum operator-(const scalar_operator &other, const product_operator &self); template -operator_sum operator-(const elementary_operator &other, const product_operator &self); +operator_sum operator-(const matrix_operator &other, const product_operator &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 8100562c9f..2a999beb8c 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -14,7 +14,7 @@ namespace cudaq { class scalar_operator; -class elementary_operator; +class matrix_operator; template class product_operator; @@ -75,54 +75,54 @@ operator_sum operator-(const HandlerTy &other, const operator_sum operator*(double other, const product_operator &self); +product_operator operator*(double other, const product_operator &self); extern template -operator_sum operator+(double other, const product_operator &self); +operator_sum operator+(double other, const product_operator &self); extern template -operator_sum operator-(double other, const product_operator &self); +operator_sum operator-(double other, const product_operator &self); extern template -product_operator operator*(std::complex other, const product_operator &self); +product_operator operator*(std::complex other, const product_operator &self); extern template -operator_sum operator+(std::complex other, const product_operator &self); +operator_sum operator+(std::complex other, const product_operator &self); extern template -operator_sum operator-(std::complex other, const product_operator &self); +operator_sum operator-(std::complex other, const product_operator &self); extern template -product_operator operator*(const scalar_operator &other, const product_operator &self); +product_operator operator*(const scalar_operator &other, const product_operator &self); extern template -operator_sum operator+(const scalar_operator &other, const product_operator &self); +operator_sum operator+(const scalar_operator &other, const product_operator &self); extern template -operator_sum operator-(const scalar_operator &other, const product_operator &self); +operator_sum operator-(const scalar_operator &other, const product_operator &self); extern template -product_operator operator*(const elementary_operator &other, const product_operator &self); +product_operator operator*(const matrix_operator &other, const product_operator &self); extern template -operator_sum operator+(const elementary_operator &other, const product_operator &self); +operator_sum operator+(const matrix_operator &other, const product_operator &self); extern template -operator_sum operator-(const elementary_operator &other, const product_operator &self); +operator_sum operator-(const matrix_operator &other, const product_operator &self); extern template -operator_sum operator*(double other, const operator_sum &self); +operator_sum operator*(double other, const operator_sum &self); extern template -operator_sum operator+(double other, const operator_sum &self); +operator_sum operator+(double other, const operator_sum &self); extern template -operator_sum operator-(double other, const operator_sum &self); +operator_sum operator-(double other, const operator_sum &self); extern template -operator_sum operator*(std::complex other, const operator_sum &self); +operator_sum operator*(std::complex other, const operator_sum &self); extern template -operator_sum operator+(std::complex other, const operator_sum &self); +operator_sum operator+(std::complex other, const operator_sum &self); extern template -operator_sum operator-(std::complex other, const operator_sum &self); +operator_sum operator-(std::complex other, const operator_sum &self); extern template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); +operator_sum operator*(const scalar_operator &other, const operator_sum &self); extern template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); +operator_sum operator+(const scalar_operator &other, const operator_sum &self); extern template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); +operator_sum operator-(const scalar_operator &other, const operator_sum &self); extern template -operator_sum operator*(const elementary_operator &other, const operator_sum &self); +operator_sum operator*(const matrix_operator &other, const operator_sum &self); extern template -operator_sum operator+(const elementary_operator &other, const operator_sum &self); +operator_sum operator+(const matrix_operator &other, const operator_sum &self); extern template -operator_sum operator-(const elementary_operator &other, const operator_sum &self); +operator_sum operator-(const matrix_operator &other, const operator_sum &self); #endif } \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 0dc767e344..726d8f64a8 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -465,7 +465,7 @@ friend class operator_sum; // FIXME: explicitly list members instead? }; -class elementary_operator { +class matrix_operator { private: static std::map m_ops; @@ -485,23 +485,23 @@ class elementary_operator { /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - elementary_operator(std::string operator_id, const std::vector °rees) + matrix_operator(std::string operator_id, const std::vector °rees) : id(operator_id), degrees(degrees) {} // constructor - elementary_operator(std::string operator_id, std::vector &°rees) + matrix_operator(std::string operator_id, std::vector &°rees) : id(operator_id), degrees(std::move(degrees)) {} // copy constructor - elementary_operator(const elementary_operator &other) + matrix_operator(const matrix_operator &other) : degrees(other.degrees), id(other.id) {} // move constructor - elementary_operator(elementary_operator &&other) + matrix_operator(matrix_operator &&other) : degrees(std::move(other.degrees)), id(other.id) {} // assignment operator - elementary_operator& operator=(const elementary_operator& other) { + matrix_operator& operator=(const matrix_operator& other) { if (this != &other) { degrees = other.degrees; id = other.id; @@ -510,23 +510,23 @@ class elementary_operator { } // move assignment operator - elementary_operator& operator=(elementary_operator &&other) { + matrix_operator& operator=(matrix_operator &&other) { degrees = std::move(other.degrees); id = other.id; return *this; } - ~elementary_operator() = default; + ~matrix_operator() = default; /// @brief The degrees of freedom that the operator acts on in canonical /// order. std::vector degrees; std::string id; - /// @brief Return the `elementary_operator` as a string. + /// @brief Return the `matrix_operator` as a string. std::string to_string() const; - /// @brief Return the `elementary_operator` as a matrix. + /// @brief Return the `matrix_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level @@ -536,22 +536,22 @@ class elementary_operator { /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(const elementary_operator &other) const { + bool operator==(const matrix_operator &other) const { return this->id == other.id && this->degrees == other.degrees; } // Predefined operators. - static product_operator identity(int degree); - static product_operator zero(int degree); - static product_operator annihilate(int degree); - static product_operator create(int degree); - static product_operator momentum(int degree); - static product_operator number(int degree); - static product_operator parity(int degree); - static product_operator position(int degree); + static product_operator identity(int degree); + static product_operator zero(int degree); + static product_operator annihilate(int degree); + static product_operator create(int degree); + static product_operator momentum(int degree); + static product_operator number(int degree); + static product_operator parity(int degree); + static product_operator position(int degree); /// Operators that accept parameters at runtime. - static product_operator squeeze(int degree); - static product_operator displace(int degree); + static product_operator squeeze(int degree); + static product_operator displace(int degree); /// @brief Adds the definition of an elementary operator with the given id to /// the class. After definition, an the defined elementary operator can be @@ -582,13 +582,13 @@ class elementary_operator { template void define(std::string operator_id, std::map expected_dimensions, Func create) { - if (elementary_operator::m_ops.find(operator_id) != elementary_operator::m_ops.end()) { + if (matrix_operator::m_ops.find(operator_id) != matrix_operator::m_ops.end()) { // todo: make a nice error message to say op already exists throw; } auto defn = Definition(); defn.create_definition(operator_id, expected_dimensions, create); - elementary_operator::m_ops[operator_id] = defn; + matrix_operator::m_ops[operator_id] = defn; } }; @@ -638,11 +638,11 @@ class rydberg_hamiltonian : public operator_sum { std::optional>> delta_local; }; #ifdef CUDAQ_INSTANTIATE_TEMPLATES -template class product_operator; -template class operator_sum; +template class product_operator; +template class operator_sum; #else -extern template class product_operator; -extern template class operator_sum; +extern template class product_operator; +extern template class operator_sum; #endif @@ -651,7 +651,7 @@ class OperatorArithmetics { public: /// @brief Accesses the relevant data to evaluate an operator expression /// in the leaf nodes, that is in elementary and scalar operators. - TEval evaluate(product_operator &op); + TEval evaluate(product_operator &op); /// @brief Adds two operators that act on the same degrees of freedom. TEval add(TEval val1, TEval val2); @@ -714,7 +714,7 @@ class MatrixArithmetics : public OperatorArithmetics { // Computes the matrix of an ElementaryOperator or ScalarOperator using its // `to_matrix` method. EvaluatedMatrix - evaluate(std::variant> op); + evaluate(std::variant> op); }; } // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 77437e5d31..7b48ef7289 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -273,8 +273,8 @@ gtest_discover_tests(test_spin) # Create an executable for operators UnitTests set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/operator_sum.cpp - dynamics/elementary_ops_simple.cpp - dynamics/elementary_ops_arithmetic.cpp + dynamics/matrix_ops_simple.cpp + dynamics/matrix_ops_arithmetic.cpp dynamics/scalar_ops_simple.cpp dynamics/scalar_ops_arithmetic.cpp dynamics/product_operators_arithmetic.cpp diff --git a/unittests/dynamics/elementary_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp similarity index 85% rename from unittests/dynamics/elementary_ops_arithmetic.cpp rename to unittests/dynamics/matrix_ops_arithmetic.cpp index 2fd90a57fe..30bda31d22 100644 --- a/unittests/dynamics/elementary_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -100,11 +100,11 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } -void assert_product_equal(const cudaq::product_operator &got, +void assert_product_equal(const cudaq::product_operator &got, const std::complex &expected_coefficient, - const std::vector &expected_terms) { + const std::vector &expected_terms) { - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); @@ -124,10 +124,10 @@ cudaq::scalar_operator negate(cudaq::scalar_operator op) { TEST(OperatorExpressions, checkElementaryAgainstDouble) { std::complex value = 0.125 + 0.125j; - // `elementary_operator` + `complex` and `complex` + - // `elementary_operator` + // `matrix_operator` + `complex` and `complex` + + // `matrix_operator` { - auto elementary = cudaq::elementary_operator::annihilate(0); + auto elementary = cudaq::matrix_operator::annihilate(0); auto sum = value + elementary; auto reverse = elementary + value; @@ -143,9 +143,9 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `elementary_operator` - `complex` and `complex` - `elementary_operator` + // `matrix_operator` - `complex` and `complex` - `matrix_operator` { - auto elementary = cudaq::elementary_operator::position(0); + auto elementary = cudaq::matrix_operator::position(0); auto difference = value - elementary; auto reverse = elementary - value; @@ -161,10 +161,10 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `elementary_operator` * `complex` and `complex` * - // `elementary_operator` + // `matrix_operator` * `complex` and `complex` * + // `matrix_operator` { - auto elementary = cudaq::elementary_operator::number(0); + auto elementary = cudaq::matrix_operator::number(0); auto product = value * elementary; auto reverse = elementary * value; @@ -192,9 +192,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { int degree_index = 0; double const_scale_factor = 2.0; - // `elementary_operator + scalar_operator` + // `matrix_operator + scalar_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); auto other = cudaq::scalar_operator(const_scale_factor); auto sum = self + other; @@ -216,9 +216,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator + scalar_operator` + // `matrix_operator + scalar_operator` { - auto self = cudaq::elementary_operator::parity(0); + auto self = cudaq::matrix_operator::parity(0); auto other = cudaq::scalar_operator(function); auto sum = self + other; @@ -241,9 +241,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator - scalar_operator` + // `matrix_operator - scalar_operator` { - auto self = cudaq::elementary_operator::number(0); + auto self = cudaq::matrix_operator::number(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `operator_sum` type. @@ -270,9 +270,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator - scalar_operator` + // `matrix_operator - scalar_operator` { - auto self = cudaq::elementary_operator::position(0); + auto self = cudaq::matrix_operator::position(0); auto other = cudaq::scalar_operator(function); auto sum = self - other; @@ -296,17 +296,17 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator * scalar_operator` + // `matrix_operator * scalar_operator` { - auto self = cudaq::elementary_operator::momentum(0); + auto self = cudaq::matrix_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::elementary_operator("momentum", {0})}); + utils_0::assert_product_equal(product, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); std::vector want_degrees = {0}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -324,17 +324,17 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - // `elementary_operator * scalar_operator` + // `matrix_operator * scalar_operator` { - auto self = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::create(0); auto other = cudaq::scalar_operator(function); // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate(), {cudaq::elementary_operator("create", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::elementary_operator("create", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); std::vector want_degrees = {0}; // ASSERT_TRUE(product.degrees() == want_degrees); @@ -363,8 +363,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Addition, same DOF. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); // Produces an `operator_sum` type. auto sum = self + other; @@ -380,8 +380,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Addition, different DOF's. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(1); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); @@ -399,8 +399,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Subtraction, same DOF. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); @@ -416,8 +416,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Subtraction, different DOF's. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(1); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); @@ -436,8 +436,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Multiplication, same DOF. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(0); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); @@ -455,8 +455,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { // Multiplication, different DOF's. { - auto self = cudaq::elementary_operator::annihilate(0); - auto other = cudaq::elementary_operator::create(1); + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); // Produces an `product_operator` type. auto product = self * other; @@ -487,13 +487,13 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { int level_count = 3; std::complex value = 0.125 + 0.5j; - /// `elementary_operator + operator_sum` and `operator_sum + - /// elementary_operator` + /// `matrix_operator + operator_sum` and `operator_sum + + /// matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -519,13 +519,13 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `elementary_operator - operator_sum` and `operator_sum - - /// elementary_operator` + /// `matrix_operator - operator_sum` and `operator_sum - + /// matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -552,13 +552,13 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `elementary_operator * operator_sum` and `operator_sum * - /// elementary_operator` + /// `matrix_operator * operator_sum` and `operator_sum * + /// matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::squeeze(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::squeeze(0) + + cudaq::matrix_operator::identity(1); auto got = self * operator_sum; auto reverse = operator_sum * self; @@ -591,11 +591,11 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `operator_sum += elementary_operator` + /// `operator_sum += matrix_operator` { - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); - operator_sum += cudaq::elementary_operator::displace(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + operator_sum += cudaq::matrix_operator::displace(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -616,11 +616,11 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_matrix, got_matrix); } - /// `operator_sum -= elementary_operator` + /// `operator_sum -= matrix_operator` { - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); - operator_sum -= cudaq::elementary_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + operator_sum -= cudaq::matrix_operator::annihilate(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -640,12 +640,12 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { // utils_0::checkEqual(want_matrix, got_matrix); } - /// `operator_sum *= elementary_operator` + /// `operator_sum *= matrix_operator` { - auto self = cudaq::elementary_operator::annihilate(0); + auto self = cudaq::matrix_operator::annihilate(0); /// Creating an arbitrary operator sum to work against. - auto operator_sum = cudaq::elementary_operator::create(0) + - cudaq::elementary_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); operator_sum *= self; diff --git a/unittests/dynamics/elementary_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp similarity index 90% rename from unittests/dynamics/elementary_ops_simple.cpp rename to unittests/dynamics/matrix_ops_simple.cpp index da8d3ace66..ac1caf4f10 100644 --- a/unittests/dynamics/elementary_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -111,7 +111,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Identity operator. { for (auto level_count : levels) { - auto id = cudaq::elementary_operator::identity(degree_index); + auto id = cudaq::matrix_operator::identity(degree_index); auto got_id = id.to_matrix({{degree_index, level_count}}); auto want_id = utils::id_matrix(level_count); utils::checkEqual(want_id, got_id); @@ -121,7 +121,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Zero operator. { for (auto level_count : levels) { - auto zero = cudaq::elementary_operator::zero(degree_index); + auto zero = cudaq::matrix_operator::zero(degree_index); auto got_zero = zero.to_matrix({{degree_index, level_count}}); auto want_zero = utils::zero_matrix(level_count); utils::checkEqual(want_zero, got_zero); @@ -131,7 +131,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Annihilation operator. { for (auto level_count : levels) { - auto annihilate = cudaq::elementary_operator::annihilate(degree_index); + auto annihilate = cudaq::matrix_operator::annihilate(degree_index); auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); auto want_annihilate = utils::annihilate_matrix(level_count); utils::checkEqual(want_annihilate, got_annihilate); @@ -141,7 +141,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Creation operator. { for (auto level_count : levels) { - auto create = cudaq::elementary_operator::create(degree_index); + auto create = cudaq::matrix_operator::create(degree_index); auto got_create = create.to_matrix({{degree_index, level_count}}); auto want_create = utils::create_matrix(level_count); utils::checkEqual(want_create, got_create); @@ -151,7 +151,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Position operator. { for (auto level_count : levels) { - auto position = cudaq::elementary_operator::position(degree_index); + auto position = cudaq::matrix_operator::position(degree_index); auto got_position = position.to_matrix({{degree_index, level_count}}); auto want_position = utils::position_matrix(level_count); utils::checkEqual(want_position, got_position); @@ -161,7 +161,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Momentum operator. { for (auto level_count : levels) { - auto momentum = cudaq::elementary_operator::momentum(degree_index); + auto momentum = cudaq::matrix_operator::momentum(degree_index); auto got_momentum = momentum.to_matrix({{degree_index, level_count}}); auto want_momentum = utils::momentum_matrix(level_count); utils::checkEqual(want_momentum, got_momentum); @@ -171,7 +171,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Number operator. { for (auto level_count : levels) { - auto number = cudaq::elementary_operator::number(degree_index); + auto number = cudaq::matrix_operator::number(degree_index); auto got_number = number.to_matrix({{degree_index, level_count}}); auto want_number = utils::number_matrix(level_count); utils::checkEqual(want_number, got_number); @@ -181,7 +181,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { // Parity operator. { for (auto level_count : levels) { - auto parity = cudaq::elementary_operator::parity(degree_index); + auto parity = cudaq::matrix_operator::parity(degree_index); auto got_parity = parity.to_matrix({{degree_index, level_count}}); auto want_parity = utils::parity_matrix(level_count); utils::checkEqual(want_parity, got_parity); @@ -192,7 +192,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto displacement = 2.0 + 1.0j; - auto displace = cudaq::elementary_operator::displace(degree_index); + auto displace = cudaq::matrix_operator::displace(degree_index); auto got_displace = displace.to_matrix({{degree_index, level_count}}, {{"displacement", displacement}}); auto want_displace = utils::displace_matrix(level_count, displacement); @@ -204,7 +204,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { { for (auto level_count : levels) { auto squeezing = 2.0 + 1.0j; - auto squeeze = cudaq::elementary_operator::squeeze(degree_index); + auto squeeze = cudaq::matrix_operator::squeeze(degree_index); auto got_squeeze = squeeze.to_matrix({{degree_index, level_count}}, {{"squeezing", squeezing}}); auto want_squeeze = utils::squeeze_matrix(level_count, squeezing); diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index eedc8fbd94..5f6bb391b5 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -117,8 +117,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // Same degrees of freedom. // { // for (auto level_count : levels) { -// auto op0 = cudaq::elementary_operator::annihilate(0); -// auto op1 = cudaq::elementary_operator::create(0); +// auto op0 = cudaq::matrix_operator::annihilate(0); +// auto op1 = cudaq::matrix_operator::create(0); // cudaq::product_operator got = op0 * op1; // auto got_matrix = got.to_matrix({{0, level_count}}, {}); @@ -134,8 +134,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // // Different degrees of freedom. // // { // // for (auto level_count : levels) { -// // auto op0 = cudaq::elementary_operator::annihilate(0); -// // auto op1 = cudaq::elementary_operator::create(1); +// // auto op0 = cudaq::matrix_operator::annihilate(0); +// // auto op1 = cudaq::matrix_operator::create(1); // // cudaq::product_operator got = op0 * op1; // // auto got_matrix = @@ -163,8 +163,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // // Different degrees of freedom, non-consecutive. // // { // // for (auto level_count : levels) { -// // auto op0 = cudaq::elementary_operator::annihilate(0); -// // auto op1 = cudaq::elementary_operator::create(2); +// // auto op0 = cudaq::matrix_operator::annihilate(0); +// // auto op1 = cudaq::matrix_operator::create(2); // // // cudaq::product_operator got = op0 * op1; // // // auto got_matrix = @@ -177,8 +177,8 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // // provided. // // { // // for (auto level_count : levels) { -// // auto op0 = cudaq::elementary_operator::annihilate(0); -// // auto op1 = cudaq::elementary_operator::create(2); +// // auto op0 = cudaq::matrix_operator::annihilate(0); +// // auto op1 = cudaq::matrix_operator::create(2); // // // cudaq::product_operator got = op0 * op1; // // // auto got_matrix = @@ -209,7 +209,7 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // { // // Identity against constant. // { -// auto id_op = cudaq::elementary_operator::identity(0); +// auto id_op = cudaq::matrix_operator::identity(0); // auto scalar_op = cudaq::scalar_operator(value_0); // // auto multiplication = scalar_op * id_op; @@ -219,7 +219,7 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, // // Identity against constant from lambda. // { -// auto id_op = cudaq::elementary_operator::identity(0); +// auto id_op = cudaq::matrix_operator::identity(0); // auto scalar_op = cudaq::scalar_operator(function); // // auto multiplication = scalar_op * id_op; @@ -235,8 +235,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -279,8 +279,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` { level_count = 2; - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -312,8 +312,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -345,8 +345,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum *= scalar_operator` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::momentum(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::momentum(2); sum *= cudaq::scalar_operator(value); @@ -383,8 +383,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum += scalar_operator` { - auto sum = cudaq::elementary_operator::parity(1) + - cudaq::elementary_operator::position(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::position(2); sum += cudaq::scalar_operator(value); @@ -417,8 +417,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum -= scalar_operator` { - auto sum = cudaq::elementary_operator::number(1) + - cudaq::elementary_operator::annihilate(2); + auto sum = cudaq::matrix_operator::number(1) + + cudaq::matrix_operator::annihilate(2); sum -= cudaq::scalar_operator(value); @@ -457,8 +457,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` and `double * operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -501,8 +501,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + double` and `double + operator_sum` { - auto original = cudaq::elementary_operator::momentum(1) + - cudaq::elementary_operator::position(2); + auto original = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::position(2); auto sum = original + double_value; auto reverse = double_value + original; @@ -534,8 +534,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - double` and `double - operator_sum` { - auto original = cudaq::elementary_operator::parity(1) + - cudaq::elementary_operator::number(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::number(2); auto difference = original - double_value; auto reverse = double_value - original; @@ -567,8 +567,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= double` { - auto sum = cudaq::elementary_operator::squeeze(1) + - cudaq::elementary_operator::squeeze(2); + auto sum = cudaq::matrix_operator::squeeze(1) + + cudaq::matrix_operator::squeeze(2); sum *= double_value; @@ -601,8 +601,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum += double_value; @@ -629,8 +629,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum -= double_value; @@ -658,8 +658,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` and `std::complex * // operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = sum * value; auto reverse = value * sum; @@ -702,8 +702,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` and `std::complex + // operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = original + value; auto reverse = value + original; @@ -736,8 +736,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` and `std::complex - // operator_sum` { - auto original = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto difference = original - value; auto reverse = value - original; @@ -769,8 +769,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= std::complex` { - auto sum = cudaq::elementary_operator::displace(1) + - cudaq::elementary_operator::parity(2); + auto sum = cudaq::matrix_operator::displace(1) + + cudaq::matrix_operator::parity(2); sum *= value; @@ -802,8 +802,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += std::complex` { - auto sum = cudaq::elementary_operator::momentum(1) + - cudaq::elementary_operator::squeeze(2); + auto sum = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::squeeze(2); sum += value; @@ -831,8 +831,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= std::complex` { - auto sum = cudaq::elementary_operator::position(1) + - cudaq::elementary_operator::number(2); + auto sum = cudaq::matrix_operator::position(1) + + cudaq::matrix_operator::number(2); sum -= value; @@ -863,11 +863,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { - auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(3); + auto sum_0 = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::create(3); auto sum = sum_0 + sum_1; @@ -920,11 +920,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum - operator_sum` { - auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::position(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::momentum(3); + auto sum_0 = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::position(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::momentum(3); auto difference = sum_0 - sum_1; @@ -977,11 +977,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum * operator_sum` { - auto sum_0 = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(3); + auto sum_0 = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::create(3); auto sum_product = sum_0 * sum_1; auto sum_product_reverse = sum_1 * sum_0; @@ -1044,11 +1044,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum *= operator_sum` { - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); - auto sum_1 = cudaq::elementary_operator::parity(0) + - cudaq::elementary_operator::annihilate(1) + - cudaq::elementary_operator::create(3); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + auto sum_1 = cudaq::matrix_operator::parity(0) + + cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::create(3); sum *= sum_1; @@ -1110,10 +1110,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum += product_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum += product; @@ -1154,10 +1154,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum -= product_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum -= product; @@ -1198,10 +1198,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum *= product_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum *= product; diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 2b8b3c04cb..9c1747998d 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -111,8 +111,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Same degrees of freedom. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(0); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; @@ -130,8 +130,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Different degrees of freedom. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(1); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(1); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -166,8 +166,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Should produce the same matrices as the above test. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(2); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -203,8 +203,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // provided. { for (auto level_count : levels) { - auto op0 = cudaq::elementary_operator::annihilate(0); - auto op1 = cudaq::elementary_operator::create(2); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -265,7 +265,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { { // Annihilation against constant. { - auto id_op = cudaq::elementary_operator::annihilate(0); + auto id_op = cudaq::matrix_operator::annihilate(0); auto scalar_op = cudaq::scalar_operator(value_0); auto got = scalar_op * id_op; @@ -278,7 +278,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { // Annihilation against constant from lambda. { - auto id_op = cudaq::elementary_operator::annihilate(1); + auto id_op = cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(function); auto got = scalar_op * id_op; @@ -297,8 +297,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + complex` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto sum = value_0 + product_op; auto reverse = product_op + value_0; @@ -335,8 +335,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + double` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto sum = 2.0 + product_op; auto reverse = product_op + 2.0; @@ -372,8 +372,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + scalar_operator` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(value_0); auto sum = scalar_op + product_op; @@ -411,8 +411,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - complex` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto difference = value_0 - product_op; auto reverse = product_op - value_0; @@ -449,8 +449,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - double` { - auto product_op = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto difference = 2.0 - product_op; auto reverse = product_op - 2.0; @@ -486,8 +486,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - scalar_operator` { - auto product_op = cudaq::elementary_operator::momentum(0) * - cudaq::elementary_operator::momentum(1); + auto product_op = cudaq::matrix_operator::momentum(0) * + cudaq::matrix_operator::momentum(1); auto scalar_op = cudaq::scalar_operator(value_0); auto difference = scalar_op - product_op; @@ -525,8 +525,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * complex` { - auto product_op = cudaq::elementary_operator::number(0) * - cudaq::elementary_operator::number(1); + auto product_op = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -567,8 +567,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * double` { - auto product_op = cudaq::elementary_operator::parity(0) * - cudaq::elementary_operator::parity(1); + auto product_op = cudaq::matrix_operator::parity(0) * + cudaq::matrix_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); @@ -608,8 +608,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * scalar_operator` { - auto product_op = cudaq::elementary_operator::position(0) * - cudaq::elementary_operator::position(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto scalar_op = cudaq::scalar_operator(value_0); auto product = scalar_op * product_op; @@ -649,8 +649,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= complex` { - auto product = cudaq::elementary_operator::number(0) * - cudaq::elementary_operator::momentum(1); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.n_terms() == 2); @@ -680,8 +680,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= double` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::create(1); product *= 2.0; ASSERT_TRUE(product.n_terms() == 2); @@ -710,8 +710,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= scalar_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(value_0); product *= scalar_op; @@ -749,10 +749,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator + product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::annihilate(2); auto sum = term_0 + term_1; @@ -800,10 +800,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator - product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::number(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::momentum(2); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::number(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -849,10 +849,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator * product_operator` { - auto term_0 = cudaq::elementary_operator::position(0) * - cudaq::elementary_operator::annihilate(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::parity(2); + auto term_0 = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::annihilate(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -898,10 +898,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator *= product_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::number(1); - auto term_1 = cudaq::elementary_operator::create(1) * - cudaq::elementary_operator::annihilate(2); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::number(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::annihilate(2); term_0 *= term_1; @@ -950,11 +950,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { int level_count = 3; - // `product_operator + elementary_operator` + // `product_operator + matrix_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); auto sum = product + elementary; auto reverse = elementary + product; @@ -985,11 +985,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `product_operator - elementary_operator` + // `product_operator - matrix_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); auto difference = product - elementary; auto reverse = elementary - product; @@ -1020,11 +1020,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `product_operator * elementary_operator` + // `product_operator * matrix_operator` { - auto term_0 = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); auto product = term_0 * elementary; auto reverse = elementary * term_0; @@ -1055,11 +1055,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `product_operator *= elementary_operator` + // `product_operator *= matrix_operator` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto elementary = cudaq::elementary_operator::create(1); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); product *= elementary; @@ -1091,10 +1091,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator + operator_sum` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto original_sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto original_sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = product + original_sum; auto reverse = original_sum + product; @@ -1141,10 +1141,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator - operator_sum` { - auto product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto original_difference = cudaq::elementary_operator::create(1) - - cudaq::elementary_operator::create(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto original_difference = cudaq::matrix_operator::create(1) - + cudaq::matrix_operator::create(2); auto difference = product - original_difference; auto reverse = original_difference - product; @@ -1191,10 +1191,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator * operator_sum` { - auto original_product = cudaq::elementary_operator::annihilate(0) * - cudaq::elementary_operator::annihilate(1); - auto sum = cudaq::elementary_operator::create(1) + - cudaq::elementary_operator::create(2); + auto original_product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto product = original_product * sum; auto reverse = sum * original_product; From d0b0d260fdbbbaad28fe67261ce072afc87d2d5b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 16:14:18 +0000 Subject: [PATCH 190/311] minor thing Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 9 +++- unittests/dynamics/matrix_ops_arithmetic.cpp | 12 ++--- .../dynamics/product_operators_arithmetic.cpp | 44 +++++++++---------- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 3debd0e6c0..6a2973bb6c 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -174,7 +174,14 @@ void operator_sum::aggregate_terms(const product_operator std::vector operator_sum::degrees() const { - throw std::runtime_error("not implemented"); + std::set unsorted_degrees; + for (const std::vector &term : this->terms) { + for (const HandlerTy &op : term) + unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); + } + auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION + return degrees; } template diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 30bda31d22..e2cd52e337 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -309,8 +309,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); std::vector want_degrees = {0}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. @@ -337,8 +337,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); std::vector want_degrees = {0}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. @@ -443,7 +443,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { ASSERT_TRUE(product.n_terms() == 2); std::vector want_degrees = {0}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); // /// Check the matrices. @@ -463,7 +463,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { ASSERT_TRUE(product.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 9c1747998d..0718b27f1a 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -307,8 +307,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(sum.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -345,8 +345,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(sum.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -383,8 +383,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(sum.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -421,8 +421,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(difference.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(difference.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -459,8 +459,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(difference.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(difference.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -497,8 +497,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.n_terms() == 2); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(difference.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(difference.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -539,8 +539,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -581,8 +581,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -621,8 +621,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); - // ASSERT_TRUE(reverse.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -657,7 +657,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -688,7 +688,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -721,7 +721,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(scalar_op.evaluate() == value_0); std::vector want_degrees = {0, 1}; - // ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. @@ -759,7 +759,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 2); std::vector want_degrees = {0, 1, 2}; - // ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(sum.degrees() == want_degrees); /// Check the matrices. /// FIXME: Comment me back in when `to_matrix` is implemented. From 144ed26922fffb01dce8184c7847a38432c5a122 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 30 Jan 2025 16:29:51 +0000 Subject: [PATCH 191/311] minor thing Signed-off-by: Bettina Heim --- unittests/dynamics/product_operators_arithmetic.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 0718b27f1a..5c2f76be57 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -102,6 +102,16 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } +void assert_product_equal(const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { + + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + ASSERT_TRUE(sumterms_prod.size() == 1); + ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); + ASSERT_TRUE(got.get_terms() == expected_terms); +} + } // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { @@ -115,6 +125,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; + utils_1::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {0}), cudaq::matrix_operator("create", {0})}); auto got_matrix = got.to_matrix({{0, level_count}}); auto matrix0 = utils_1::annihilate_matrix(level_count); From aa989694f5f8d098122f24f18f9b185c1a8acd4b Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 31 Jan 2025 11:05:45 +0000 Subject: [PATCH 192/311] minor things Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/helpers.cpp | 24 ++++---- runtime/cudaq/dynamics/helpers.h | 32 ++++++++++ runtime/cudaq/dynamics/manipulation.cpp | 8 +-- runtime/cudaq/dynamics/operator_sum.cpp | 4 +- runtime/cudaq/dynamics/product_operators.cpp | 7 +-- unittests/dynamics/matrix_ops_arithmetic.cpp | 11 ++-- unittests/dynamics/matrix_ops_simple.cpp | 9 ++- unittests/dynamics/operator_sum.cpp | 9 ++- .../dynamics/product_operators_arithmetic.cpp | 58 +++++++++++++------ 9 files changed, 111 insertions(+), 51 deletions(-) create mode 100644 runtime/cudaq/dynamics/helpers.h diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 47c780a988..c5fcce1cbe 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -14,12 +14,15 @@ #include #include +namespace cudaq { +namespace detail { + class _OperatorHelpers { public: _OperatorHelpers() = default; // Aggregate parameters from multiple mappings. - static std::map aggregate_parameters( + std::map aggregate_parameters( const std::vector> ¶meter_mappings) { std::map parameter_descriptions; @@ -37,7 +40,7 @@ class _OperatorHelpers { } // Extract documentation for a specific parameter from docstring. - static std::string parameter_docs(const std::string ¶m_name, + std::string parameter_docs(const std::string ¶m_name, const std::string &docs) { if (param_name.empty() || docs.empty()) { return ""; @@ -69,7 +72,7 @@ class _OperatorHelpers { } // Extract positional arguments and keyword-only arguments. - static std::pair, std::map> + std::pair, std::map> args_from_kwargs( const std::map &kwargs, const std::vector &required_args, @@ -96,7 +99,7 @@ class _OperatorHelpers { /// Generates all possible states for the given dimensions ordered according /// to the sequence of degrees (ordering is relevant if dimensions differ). - static std::vector + std::vector generate_all_states(std::vector degrees, std::map dimensions) { if (degrees.size() == 0) return {}; @@ -121,12 +124,7 @@ class _OperatorHelpers { return states; } - // Permutes the given matrix according to the given permutation. - // If states is the current order of vector entries on which the given matrix - // acts, and permuted_states is the desired order of an array on which the - // permuted matrix should act, then the permutation is defined such that - // [states[i] for i in permutation] produces permuted_states. - static cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, std::vector permutation) { auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); std::vector> sorted_values; @@ -145,9 +143,11 @@ class _OperatorHelpers { return result; } - // Returns the degrees sorted in canonical order. - static std::vector canonicalize_degrees(std::vector degrees) { + std::vector canonicalize_degrees(std::vector degrees) { std::sort(degrees.begin(), degrees.end(), std::greater()); return degrees; } + }; +} +} diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h new file mode 100644 index 0000000000..5f336847a5 --- /dev/null +++ b/runtime/cudaq/dynamics/helpers.h @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include "cudaq/utils/tensor.h" + +namespace cudaq { +namespace detail { + + /// Generates all possible states for the given dimensions ordered according + /// to the sequence of degrees (ordering is relevant if dimensions differ). + std::vector generate_all_states(std::vector degrees, std::map dimensions); + + // Permutes the given matrix according to the given permutation. + // If states is the current order of vector entries on which the given matrix + // acts, and permuted_states is the desired order of an array on which the + // permuted matrix should act, then the permutation is defined such that + // [states[i] for i in permutation] produces permuted_states. + cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation); + + // Returns the degrees sorted in canonical order. + std::vector canonicalize_degrees(std::vector degrees); +} +} + diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 8c245f818d..26492f2ec5 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" -#include "helpers.cpp" +#include "helpers.h" namespace cudaq { @@ -15,7 +15,7 @@ std::vector MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::vector canon_degrees) { auto states = - _OperatorHelpers::generate_all_states(canon_degrees, m_dimensions); + cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; for (auto degree : op_degrees) @@ -45,12 +45,12 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::tuple> MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, std::vector op_degrees) { - auto canon_degrees = _OperatorHelpers::canonicalize_degrees(op_degrees); + auto canon_degrees = cudaq::detail::canonicalize_degrees(op_degrees); if (op_degrees == canon_degrees) return std::tuple>{op_matrix, canon_degrees}; auto permutation = this->_compute_permutation(op_degrees, canon_degrees); - auto result = _OperatorHelpers::permute_matrix(op_matrix, permutation); + auto result = cudaq::detail::permute_matrix(op_matrix, permutation); return std::tuple>{result, canon_degrees}; } diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 6a2973bb6c..2f32e565a5 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "helpers.h" #include #include @@ -180,8 +181,7 @@ std::vector operator_sum::degrees() const { unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); - std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION - return degrees; + return cudaq::detail::canonicalize_degrees(degrees); } template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 87c8237f2e..22f31a52c7 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" -#include "cudaq/dynamics/helpers.cpp" +#include "helpers.h" #include #include @@ -77,7 +77,7 @@ cudaq::matrix_2 product_operator::m_evaluate( noncanon_set.insert(degree); } } - auto degrees = _OperatorHelpers::canonicalize_degrees(noncanon_degrees); + auto degrees = cudaq::detail::canonicalize_degrees(noncanon_degrees); auto evaluated = EvaluatedMatrix(degrees, _padded_op(arithmetics, terms[0], degrees, dimensions, parameters)); @@ -139,8 +139,7 @@ std::vector product_operator::degrees() const { unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); - std::sort(degrees.begin(), degrees.end()); // FIXME: DELEGATE ANY CONVENTION RELATED ORDERING TO A GENERAL HELPER FUNCTION - return degrees; + return cudaq::detail::canonicalize_degrees(degrees); } template diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index e2cd52e337..95ec494d29 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -17,9 +17,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } @@ -462,7 +465,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp index ac1caf4f10..117fe51f48 100644 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -17,9 +17,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 5f6bb391b5..f3e649cc80 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -17,9 +17,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 5c2f76be57..a16036e910 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -19,9 +19,12 @@ void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_val = a[{i, j}].real(); - double b_val = b[{i, j}].real(); - EXPECT_NEAR(a_val, b_val, 1e-8); + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); } } } @@ -112,6 +115,15 @@ void assert_product_equal(const cudaq::product_operator ASSERT_TRUE(got.get_terms() == expected_terms); } +void print(cudaq::matrix_2 matrix) { + for (std::size_t i = 0; i < matrix.get_rows(); i++) { + for (std::size_t j = 0; j < matrix.get_columns(); j++) { + std::cout << matrix[{i,j}] << " "; + } + std::cout << std::endl; + } +} + } // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { @@ -131,6 +143,14 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto matrix0 = utils_1::annihilate_matrix(level_count); auto matrix1 = utils_1::create_matrix(level_count); auto want_matrix = matrix0 * matrix1; + utils_1::print(matrix1); + std::cout << std::endl; + utils_1::print(matrix0); + std::cout << std::endl; + utils_1::print(want_matrix); + std::cout << std::endl; + utils_1::print(got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; @@ -147,7 +167,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -183,7 +203,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {0, 2}; + std::vector want_degrees = {2, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -220,7 +240,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {0, 2}; + std::vector want_degrees = {2, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -317,7 +337,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -355,7 +375,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -393,7 +413,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -431,7 +451,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -469,7 +489,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -507,7 +527,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -549,7 +569,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -591,7 +611,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -631,7 +651,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -667,7 +687,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. @@ -698,7 +718,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. @@ -731,7 +751,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(scalar_op.evaluate() == value_0); - std::vector want_degrees = {0, 1}; + std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); /// Check the matrices. @@ -769,7 +789,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 2); - std::vector want_degrees = {0, 1, 2}; + std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); /// Check the matrices. From 9591195e271d2518b89588889b23edcb38678d84 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 31 Jan 2025 20:27:05 +0000 Subject: [PATCH 193/311] enabling a whole bunch of additional tests Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 22 +- runtime/cudaq/dynamics/definition.cpp | 13 +- runtime/cudaq/dynamics/matrix_operators.cpp | 106 +++--- runtime/cudaq/dynamics/operator_sum.cpp | 11 +- runtime/cudaq/dynamics/product_operators.cpp | 108 ++---- runtime/cudaq/operators.h | 28 +- unittests/dynamics/matrix_ops_arithmetic.cpp | 174 +++------ .../dynamics/product_operators_arithmetic.cpp | 339 +++++------------- 8 files changed, 252 insertions(+), 549 deletions(-) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index 444173d941..ccc394c9b9 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -143,32 +143,20 @@ class ScalarCallbackFunction : CallbackFunction { /// or scalar operator is instantiated by other means than the `define` /// class method. class Definition { -public: +private: std::string id; - - // The user-provided generator function should take a variable number of - // complex doubles for the parameters. It should return a - // `cudaq::tensor` type representing the operator - // matrix. CallbackFunction generator; + std::map m_expected_dimensions; - // Constructor. - Definition(); +public: - // Destructor. + Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create); + Definition(Definition &&def); ~Definition(); - void create_definition(const std::string &operator_id, - std::map expected_dimensions, - CallbackFunction &&create); - // To call the generator function matrix_2 generate_matrix( const std::map °rees, const std::map> ¶meters) const; - -private: - // Member variables - std::map m_expected_dimensions; }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp index cc357fbeab..0a9c1791b2 100644 --- a/runtime/cudaq/dynamics/definition.cpp +++ b/runtime/cudaq/dynamics/definition.cpp @@ -16,16 +16,11 @@ namespace cudaq { -Definition::Definition() = default; +Definition::Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create) + : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} -// Convenience setter -void Definition::create_definition(const std::string &operator_id, - std::map expected_dimensions, - CallbackFunction &&create) { - id = operator_id; - generator = std::move(create); - m_expected_dimensions = std::move(expected_dimensions); -} +Definition::Definition(Definition &&def) + : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( const std::map °rees, diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 4e7a7a6bcb..ee171fc7c1 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -18,11 +18,8 @@ std::map matrix_operator::m_ops = {}; product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -33,18 +30,16 @@ product_operator matrix_operator::identity(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, std::move(func)); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when @@ -53,18 +48,16 @@ product_operator matrix_operator::zero(int degree) { auto mat = matrix_2(dimension, dimension); return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -73,18 +66,16 @@ product_operator matrix_operator::annihilate(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::create(int degree) { std::string op_id = "create"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -93,18 +84,16 @@ product_operator matrix_operator::create(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::position(int degree) { std::string op_id = "position"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -117,18 +106,16 @@ product_operator matrix_operator::position(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -141,18 +128,16 @@ product_operator matrix_operator::momentum(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::number(int degree) { std::string op_id = "number"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -161,18 +146,16 @@ product_operator matrix_operator::number(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> _none) { std::size_t dimension = dimensions[degree]; auto mat = matrix_2(dimension, dimension); @@ -181,18 +164,16 @@ product_operator matrix_operator::parity(int degree) { } return mat; }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> parameters) { std::size_t dimension = dimensions[degree]; auto displacement_amplitude = parameters["displacement"]; @@ -207,19 +188,17 @@ product_operator matrix_operator::displace(int degree) { auto term2 = std::conj(displacement_amplitude) * annihilate; return (term1 - term2).exponential(); }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; - auto op = matrix_operator(op_id, {degree}); - // A dimension of -1 indicates this operator can act on any dimension. - op.expected_dimensions[degree] = -1; - if (op.m_ops.find(op_id) == op.m_ops.end()) { - auto func = [&, degree](std::map dimensions, + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [degree](std::map dimensions, std::map> parameters) { std::size_t dimension = dimensions[degree]; auto squeezing = parameters["squeezing"]; @@ -235,8 +214,9 @@ product_operator matrix_operator::squeeze(int degree) { auto difference = 0.5 * (term1 - term2); return difference.exponential(); }; - op.define(op_id, op.expected_dimensions, func); + matrix_operator::define(op_id, {{degree, -1}}, func); } + auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); } @@ -244,7 +224,11 @@ product_operator matrix_operator::squeeze(int degree) { matrix_2 matrix_operator::to_matrix( std::map dimensions, std::map> parameters) const { - return m_ops[id].generator(dimensions, parameters); + auto it = matrix_operator::m_ops.find(this->id); + if (it != matrix_operator::m_ops.end()) { + return it->second.generate_matrix(dimensions, parameters); + } + throw std::range_error("unable to find operator"); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 2f32e565a5..8fc83fdd32 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -52,23 +52,20 @@ cudaq::matrix_2 operator_sum::m_evaluate( auto sum = EvaluatedMatrix(); if (pad_terms) { - sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, dimensions, - parameters, pad_terms)); + sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; - auto eval = paddedTerm(term).m_evaluate(arithmetics, dimensions, - parameters, pad_terms); + auto eval = paddedTerm(term).m_evaluate(arithmetics, pad_terms); sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); } } else { sum = - EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, dimensions, - parameters, pad_terms)); + EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; auto eval = - term.m_evaluate(arithmetics, dimensions, parameters, pad_terms); + term.m_evaluate(arithmetics, pad_terms); sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); } } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 22f31a52c7..0489eb9a4c 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -13,102 +13,70 @@ #include #include #include +#include namespace cudaq { // private methods -cudaq::matrix_2 -_padded_op(cudaq::MatrixArithmetics arithmetics, cudaq::matrix_operator op, +EvaluatedMatrix +_padded_op(MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, std::vector degrees, std::map dimensions, std::map> parameters) { /// Creating the tensor product with op being last is most efficient. - std::vector padded; + std::vector padded; for (const auto °ree : degrees) { - if (std::find(op.degrees.begin(), op.degrees.end(), degree) == - op.degrees.end(), - degree) { - padded.push_back( - arithmetics.evaluate(cudaq::matrix_operator::identity(degree)) - .matrix()); + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == op.degrees.end()) { + // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS ARE REQUIRED. + padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); + // FIXME: avoid creation of a product here - + // but we need to make sure identity is defined before using it (all ops are lazily defined...) + //padded.push_back(cudaq::matrix_operator("identity", {degree}).to_matrix()); } - matrix_2 mat = op.to_matrix(dimensions, parameters); - padded.push_back(mat); } - /// FIXME: This directly uses cudaq::kronecker instead of the tensor method. - /// I need to double check to make sure this gives the equivalent behavior - /// to the method used in python. - return cudaq::kronecker(padded.begin(), padded.end()); - ; + matrix_2 mat = op.to_matrix(dimensions, parameters); + auto res = EvaluatedMatrix(op.degrees, mat); // FIXME: PUT THIS LAST + for(auto &op : padded) + res = arithmetics.tensor(res, op); + return res; } +// FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - +// IT SUPPOSED TO TAKE A TRANSFORMATION (ANY OPERATOR ARITHMETICS) AND APPLY IT template cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const { - /// Grab the underlying elementary operators. - auto terms = this->get_terms(); - - std::set noncanon_set; - for (const auto &op : terms) { - for (const auto °ree : op.degrees) { - noncanon_set.insert(degree); - } - } - std::vector noncanon_degrees(noncanon_set.begin(), noncanon_set.end()); - - // Calculate the total dimensions of the Hilbert space to create our - // identity matrix. - auto full_hilbert_size = 1; - for (const auto degree : noncanon_degrees) - full_hilbert_size *= dimensions[degree]; - cudaq::matrix_2 result(full_hilbert_size, full_hilbert_size); - // If this product operator consists only of scalar operator terms, - // we will avoid all of the below logic and just return the scalar value - // stored in an identity matrix spanning the full Hilbert space of the - // provided `dimensions`. + MatrixArithmetics arithmetics, bool pad_terms) const { + const std::vector &terms = this->terms[0]; + auto degrees = this->degrees(); + cudaq::matrix_2 result; + if (terms.size() > 0) { if (pad_terms) { - // Sorting the degrees to avoid unnecessary permutations during the - // padding. - std::set noncanon_set; - for (const auto &op : terms) { - for (const auto °ree : op.degrees) { - noncanon_set.insert(degree); - } - } - auto degrees = cudaq::detail::canonicalize_degrees(noncanon_degrees); - auto evaluated = - EvaluatedMatrix(degrees, _padded_op(arithmetics, terms[0], - degrees, dimensions, parameters)); - + auto evaluated = _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto op = terms[op_idx]; - if (op.degrees.size() != 1) { - auto padded_op_to_print = - _padded_op(arithmetics, op, degrees, dimensions, parameters); - auto padded_mat = - EvaluatedMatrix(degrees, _padded_op(arithmetics, op, degrees, - dimensions, parameters)); - evaluated = arithmetics.mul(evaluated, padded_mat); + const HandlerTy &op = terms[op_idx]; + if (op.degrees.size() != 1 || op != cudaq::matrix_operator("identity", op.degrees)) { + auto padded = _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = arithmetics.mul(evaluated, padded); } } result = evaluated.matrix(); } else { auto evaluated = arithmetics.evaluate(terms[0]); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto op = terms[op_idx]; - auto mat = op.to_matrix(dimensions, parameters); - evaluated = - arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + auto &op = terms[op_idx]; + auto mat = op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); } result = evaluated.matrix(); } } else { + auto full_hilbert_size = 1; + for (const auto degree : degrees) + full_hilbert_size *= arithmetics.m_dimensions[degree]; result = cudaq::matrix_2::identity(full_hilbert_size); } - auto coefficient = this->get_coefficient(); - return coefficient.evaluate(parameters) * result; + return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } template @@ -159,8 +127,7 @@ scalar_operator product_operator::get_coefficient() const { template cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const; + MatrixArithmetics arithmetics, bool pad_terms) const; template std::vector product_operator::degrees() const; @@ -276,10 +243,7 @@ std::string product_operator::to_string() const { template matrix_2 product_operator::to_matrix(std::map dimensions, std::map> parameters) const { - if (this->get_coefficient() != scalar_operator(1.) || this->n_terms() != 1) - return this->m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, - parameters); - return this->get_terms()[0].to_matrix(dimensions, parameters); + return this->m_evaluate(MatrixArithmetics(dimensions, parameters)); } template diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 726d8f64a8..c50f3df218 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -331,8 +331,7 @@ friend class operator_sum; // FIXME: explicitly list members instead? template void aggregate_terms(const HandlerTy &head, Args&& ... args); - matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; public: @@ -470,14 +469,6 @@ class matrix_operator { private: static std::map m_ops; -protected: - // FIXME: revise implementation - /// @brief The number of levels, that is the dimension, for each degree of - /// freedom in canonical order that the operator acts on. A value of zero or - /// less indicates that the operator is defined for any dimension of that - /// degree. - std::map expected_dimensions; - public: // The constructor should never be called directly by the user: // Keeping it internally documented for now, however. @@ -579,16 +570,14 @@ class matrix_operator { /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - template - void define(std::string operator_id, std::map expected_dimensions, - Func create) { - if (matrix_operator::m_ops.find(operator_id) != matrix_operator::m_ops.end()) { + static void define(std::string operator_id, std::map expected_dimensions, + CallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); + auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); + if (!result.second) { // todo: make a nice error message to say op already exists throw; } - auto defn = Definition(); - defn.create_definition(operator_id, expected_dimensions, create); - matrix_operator::m_ops[operator_id] = defn; } }; @@ -690,14 +679,15 @@ friend class MatrixArithmetics; /// of an operator expression. class MatrixArithmetics : public OperatorArithmetics { private: - std::map m_dimensions; - std::map> m_parameters; std::vector _compute_permutation(std::vector op_degrees, std::vector canon_degrees); std::tuple> _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); public: + std::map &m_dimensions; // fixme: make const + std::map> &m_parameters; // fixme: make const + MatrixArithmetics(std::map dimensions, std::map> parameters) : m_dimensions(dimensions), m_parameters(parameters) {} diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 95ec494d29..ede78fac53 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -206,17 +206,15 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::annihilate_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator + scalar_operator` @@ -230,18 +228,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, - // {{"value", const_scale_factor}}); - // auto got_reverse_matrix = reverse.to_matrix( - // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::parity_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator - scalar_operator` @@ -249,28 +243,19 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::matrix_operator::number(0); auto other = cudaq::scalar_operator(const_scale_factor); - // Produces an `operator_sum` type. auto sum = self - other; auto reverse = other - self; ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}); - // auto got_reverse_matrix = reverse.to_matrix({{degree_index, - // level_count}}); - // auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = - scaled_identity - utils_0::number_matrix(level_count); - // std::cout << "\nwant = \n" << want_matrix.dump() << "\n"; - // std::cout << "\ngot = \n" << got_matrix.dump() << "\n"; - // std::cout << "\nwant reverse = \n" << want_reverse_matrix.dump() << "\n"; - // std::cout << "\ngot reverse = \n" << got_reverse_matrix.dump() << "\n"; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils_0::number_matrix(level_count); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator - scalar_operator` @@ -284,19 +269,13 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = sum.to_matrix({{degree_index, level_count}}, - // {{"value", const_scale_factor}}); - // auto got_reverse_matrix = - // reverse.to_matrix({{degree_index, level_count}}, {{"value", - // const_scale_factor}}); auto want_matrix = - // utils_0::position_matrix(level_count) + scaled_identity; - auto want_reverse_matrix = - scaled_identity + utils_0::position_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils_0::position_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils_0::position_matrix(level_count); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } // `matrix_operator * scalar_operator` @@ -304,7 +283,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::matrix_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); - // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; @@ -315,8 +293,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); auto got_matrix = product.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); @@ -332,7 +308,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto self = cudaq::matrix_operator::create(0); auto other = cudaq::scalar_operator(function); - // Produces an `product_operator` type. auto product = self * other; auto reverse = other * self; @@ -343,18 +318,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - // auto got_matrix = product.to_matrix({{degree_index, level_count}}, - // {{"value", const_scale_factor}}); - // auto got_reverse_matrix = reverse.to_matrix( - // {{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = scaled_identity * utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } } @@ -369,12 +340,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::matrix_operator::annihilate(0); auto other = cudaq::matrix_operator::create(0); - // Produces an `operator_sum` type. auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. - auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) + utils_0::create_matrix(level_count); @@ -389,15 +357,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}); + //auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full + create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + //utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, same DOF. @@ -408,13 +375,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) - utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, different DOF's. @@ -425,16 +389,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); - /// Check the matrices. - auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = sum.to_matrix({{0, level_count}}); + //auto got_matrix = sum.to_matrix({{0, level_count}}); auto want_matrix = annihilate_full - create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + //utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, same DOF. @@ -448,12 +410,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); - // /// Check the matrices. - - // auto got_matrix = product.to_matrix({{0, level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}}); auto want_matrix = utils_0::annihilate_matrix(level_count) * utils_0::create_matrix(level_count); - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, different DOF's. @@ -468,17 +428,14 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto annihilate_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = product.to_matrix({{0, level_count}}, {}); + //auto got_matrix = product.to_matrix({{0, level_count}}, {}); auto want_matrix = annihilate_full * create_full; - // utils_0::checkEqual(want_matrix, got_matrix); + //utils_0::checkEqual(want_matrix, got_matrix); } } @@ -494,7 +451,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -504,8 +460,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(got.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -513,20 +467,18 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - // auto got_reverse_matrix = - // reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } /// `matrix_operator - operator_sum` and `operator_sum - /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -536,9 +488,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(got.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -546,20 +495,18 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {}); auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } /// `matrix_operator * operator_sum` and `operator_sum * /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::squeeze(0) + cudaq::matrix_operator::identity(1); @@ -573,9 +520,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { for (auto &term : reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = @@ -585,13 +529,12 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - // auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - // {{"squeezing", value}}); auto got_reverse_matrix = reverse.to_matrix({{0, - // level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; - // utils_0::checkEqual(want_matrix, got_matrix); - // utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); + utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } /// `operator_sum += matrix_operator` @@ -602,9 +545,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(operator_sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::displace_matrix(level_count, value)); @@ -613,10 +553,9 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {{"displacement", value}}); + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } /// `operator_sum -= matrix_operator` @@ -627,9 +566,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { ASSERT_TRUE(operator_sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -637,16 +573,14 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = term_0_full + term_1_full - self_full; - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } /// `operator_sum *= matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - /// Creating an arbitrary operator sum to work against. auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -656,9 +590,6 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { for (auto &term : operator_sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::annihilate_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), @@ -667,9 +598,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - // auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, - // level_count}}, {}); + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = sum_full * self_full; - // utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index a16036e910..18e26be328 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -143,15 +143,7 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto matrix0 = utils_1::annihilate_matrix(level_count); auto matrix1 = utils_1::create_matrix(level_count); auto want_matrix = matrix0 * matrix1; - utils_1::print(matrix1); - std::cout << std::endl; - utils_1::print(matrix0); - std::cout << std::endl; - utils_1::print(want_matrix); - std::cout << std::endl; - utils_1::print(got_matrix); - - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; ASSERT_TRUE(got.degrees() == want_degrees); @@ -171,25 +163,20 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // got.to_matrix({{0, level_count}, {1, level_count}}, {}); - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, {}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - // auto identity = utils_1::id_matrix(level_count); - // auto matrix0 = utils_1::annihilate_matrix(level_count); - // auto matrix1 = utils_1::create_matrix(level_count); + auto identity = utils_1::id_matrix(level_count); + auto matrix0 = utils_1::annihilate_matrix(level_count); + auto matrix1 = utils_1::create_matrix(level_count); - // auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - // auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - // auto want_matrix = fullHilbert0 * fullHilbert1; - // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -207,14 +194,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}, - // {}); - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0,level_count},{2,level_count}}, - // {}); + //auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -225,8 +206,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -244,21 +225,13 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, {}); - // auto got_matrix_reverse = - // got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, - // {}); + //auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); auto matrix1 = utils_1::create_matrix(level_count); - /// Identity pad the operators to compute the kronecker - /// product to the full hilbert space. std::vector matrices_0; std::vector matrices_1; matrices_0 = {identity, identity, matrix0}; @@ -271,8 +244,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(got_matrix, want_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(got_matrix, want_matrix); } } } @@ -341,27 +314,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), utils_1::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = value_0 * utils_1::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + double` @@ -379,13 +346,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -397,8 +359,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + scalar_operator` @@ -417,13 +379,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -436,8 +393,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - complex` @@ -455,13 +412,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -474,8 +426,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - double` @@ -493,13 +445,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -511,8 +458,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - scalar_operator` @@ -531,13 +478,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::momentum_matrix(level_count)); @@ -550,8 +492,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * complex` @@ -573,13 +515,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -592,8 +529,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * double` @@ -615,13 +552,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse_reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::parity_matrix(level_count)); @@ -633,8 +565,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * scalar_operator` @@ -655,13 +587,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::position_matrix(level_count)); @@ -674,8 +601,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator *= complex` @@ -690,11 +617,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -706,7 +629,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= double` @@ -721,11 +644,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -736,7 +655,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } /// `product_operator *= scalar_operator` @@ -754,11 +673,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -770,7 +685,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto want_matrix = product_matrix * scaled_identity; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } } @@ -792,11 +707,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); + //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); // Build up each individual term, cast to the full Hilbert space of the // system. @@ -826,7 +737,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix + term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` @@ -840,15 +751,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(difference.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}, - // {2,level_count+1}}, {}); + //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -875,7 +779,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix - term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` @@ -889,15 +793,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(product.n_terms() == 4); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, - // {}); + //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -924,7 +821,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` @@ -938,12 +835,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(term_0.n_terms() == 4); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, - // {}); + //auto got_matrix = term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); // Build up each individual term, cast to the full Hilbert space of the // system. @@ -973,7 +865,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix, got_matrix); } } @@ -993,13 +885,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1012,8 +899,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix + elementary_matrix; auto want_matrix_reverse = elementary_matrix + product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - matrix_operator` @@ -1028,13 +915,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1047,8 +929,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix - elementary_matrix; auto want_matrix_reverse = elementary_matrix - product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * matrix_operator` @@ -1063,13 +945,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1082,8 +959,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix * elementary_matrix; auto want_matrix_reverse = elementary_matrix * product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator *= matrix_operator` @@ -1096,11 +973,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count}}, {}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -1112,7 +985,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix = product_matrix * elementary_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } } @@ -1133,14 +1006,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, {}); - // auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); + //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1166,8 +1033,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix + sum_matrix; auto want_matrix_reverse = sum_matrix + product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` @@ -1183,14 +1050,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); + //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1216,8 +1077,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix - difference_matrix; auto want_matrix_reverse = difference_matrix - product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -1233,14 +1094,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = - // product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); auto got_matrix_reverse = - // reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}, - // {}); + //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1266,7 +1121,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix * sum_matrix; auto want_matrix_reverse = sum_matrix * product_matrix; - // utils_1::checkEqual(want_matrix, got_matrix); - // utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + //utils_1::checkEqual(want_matrix, got_matrix); + //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } From 28b748f887824924879a52684460b50db03f480d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 15:27:17 +0000 Subject: [PATCH 194/311] some more product op fixes Signed-off-by: Bettina Heim --- runtime/cudaq/definition.h | 22 +++---- runtime/cudaq/dynamics/definition.cpp | 6 +- runtime/cudaq/dynamics/helpers.cpp | 1 - runtime/cudaq/dynamics/manipulation.cpp | 22 ++++--- runtime/cudaq/dynamics/matrix_operators.cpp | 66 ++++++++++--------- runtime/cudaq/operators.h | 5 +- unittests/dynamics/matrix_ops_arithmetic.cpp | 19 +++--- .../dynamics/product_operators_arithmetic.cpp | 12 ++-- 8 files changed, 79 insertions(+), 74 deletions(-) diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index ccc394c9b9..1421e92dc0 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -20,16 +20,14 @@ namespace cudaq { -// Limit the signature of the users callback function to accept a vector of ints -// for the degree of freedom dimensions, and a vector of complex doubles for the -// concrete parameter values. using Func = std::function, std::map>)>; + std::vector, std::map>)>; class CallbackFunction { private: - // The user provided callback function that takes the degrees of - // freedom and a vector of complex parameters. + // The user provided callback function that takes a vector defining the + // dimension for each degree of freedom it acts on, and a map of complex + // parameters. Func _callback_func; public: @@ -38,7 +36,7 @@ class CallbackFunction { template CallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v, + std::is_invocable_r_v, std::map>>, "Invalid callback function. Must have signature " "matrix_2(" @@ -76,9 +74,9 @@ class CallbackFunction { bool operator!() { return (!_callback_func); } matrix_2 - operator()(std::map degrees, + operator()(std::vector relevant_dimensions, std::map> parameters) const { - return _callback_func(std::move(degrees), std::move(parameters)); + return _callback_func(std::move(relevant_dimensions), std::move(parameters)); } }; @@ -146,17 +144,17 @@ class Definition { private: std::string id; CallbackFunction generator; - std::map m_expected_dimensions; + std::vector m_expected_dimensions; public: - Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create); + Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create); Definition(Definition &&def); ~Definition(); // To call the generator function matrix_2 generate_matrix( - const std::map °rees, + const std::vector &relevant_dimensions, const std::map> ¶meters) const; }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp index 0a9c1791b2..c285a788ce 100644 --- a/runtime/cudaq/dynamics/definition.cpp +++ b/runtime/cudaq/dynamics/definition.cpp @@ -16,16 +16,16 @@ namespace cudaq { -Definition::Definition(const std::string &operator_id, std::map expected_dimensions, CallbackFunction &&create) +Definition::Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create) : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} Definition::Definition(Definition &&def) : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( - const std::map °rees, + const std::vector &relevant_dimensions, const std::map> ¶meters) const { - return generator(degrees, parameters); + return generator(relevant_dimensions, parameters); } Definition::~Definition() = default; diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index c5fcce1cbe..f8ab19f138 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -111,7 +111,6 @@ class _OperatorHelpers { } for (auto degree = degrees.begin() + 1; degree != degrees.end(); ++degree) { - std::string term; std::vector result; for (auto current : states) { for (auto state = 0; state < dimensions[degrees[*degree]]; state++) { diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 26492f2ec5..1acbbbeb6c 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -18,9 +18,10 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; - for (auto degree : op_degrees) - reordering.push_back(canon_degrees[degree]); - + for (auto degree : op_degrees) { + auto it = std::find(canon_degrees.begin(), canon_degrees.end(), degree); + reordering.push_back(it - canon_degrees.begin()); + } std::vector result; for (auto state : states) { int index; @@ -29,9 +30,7 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, term += state[i]; } auto it = std::find(states.begin(), states.end(), term); - if (it != states.end()) - index = std::distance(states.begin(), it); - result.push_back(index); + result.push_back(it - states.begin()); } return result; @@ -60,9 +59,14 @@ EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, // assert len(frozenset(op1.degrees).intersection(op2.degrees)) == 0, \ // "Operators should not have common degrees of freedom." - auto op_degrees = op1.m_degrees; - std::copy(op2.m_degrees.begin(), op2.m_degrees.end(), - back_inserter(op_degrees)); + auto op1_deg = std::move(op1.degrees()); + auto op2_deg = std::move(op2.degrees()); + std::vector op_degrees; + op_degrees.reserve(op1_deg.size() + op2_deg.size()); + for (auto d : op1_deg) + op_degrees.push_back(d); + for (auto d : op2_deg) + op_degrees.push_back(d); auto op_matrix = cudaq::kronecker(op1.m_matrix, op2.m_matrix); auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); return EvaluatedMatrix(new_degrees, new_matrix); diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index ee171fc7c1..b8926ba470 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -19,9 +19,9 @@ std::map matrix_operator::m_ops = {}; product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // Build up the identity matrix. @@ -30,7 +30,7 @@ product_operator matrix_operator::identity(int degree) { } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, std::move(func)); + matrix_operator::define(op_id, {-1}, std::move(func)); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -39,16 +39,16 @@ product_operator matrix_operator::identity(int degree) { product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when // the user invokes this later on via, e.g, `to_matrix()`. - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -57,16 +57,16 @@ product_operator matrix_operator::zero(int degree) { product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -75,16 +75,16 @@ product_operator matrix_operator::annihilate(int degree) { product_operator matrix_operator::create(int degree) { std::string op_id = "create"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -93,9 +93,9 @@ product_operator matrix_operator::create(int degree) { product_operator matrix_operator::position(int degree) { std::string op_id = "position"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -106,7 +106,7 @@ product_operator matrix_operator::position(int degree) { } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -115,9 +115,9 @@ product_operator matrix_operator::position(int degree) { product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -128,7 +128,7 @@ product_operator matrix_operator::momentum(int degree) { } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -137,16 +137,16 @@ product_operator matrix_operator::momentum(int degree) { product_operator matrix_operator::number(int degree) { std::string op_id = "number"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = static_cast(i) + 0.0j; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -155,16 +155,16 @@ product_operator matrix_operator::number(int degree) { product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> _none) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; } return mat; }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -173,9 +173,9 @@ product_operator matrix_operator::parity(int degree) { product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> parameters) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto displacement_amplitude = parameters["displacement"]; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); @@ -188,7 +188,7 @@ product_operator matrix_operator::displace(int degree) { auto term2 = std::conj(displacement_amplitude) * annihilate; return (term1 - term2).exponential(); }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -198,9 +198,9 @@ product_operator matrix_operator::displace(int degree) { product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [degree](std::map dimensions, + auto func = [](std::vector dimensions, std::map> parameters) { - std::size_t dimension = dimensions[degree]; + std::size_t dimension = dimensions[0]; auto squeezing = parameters["squeezing"]; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); @@ -214,7 +214,7 @@ product_operator matrix_operator::squeeze(int degree) { auto difference = 0.5 * (term1 - term2); return difference.exponential(); }; - matrix_operator::define(op_id, {{degree, -1}}, func); + matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); return product_operator(1., op); @@ -226,7 +226,11 @@ matrix_2 matrix_operator::to_matrix( std::map> parameters) const { auto it = matrix_operator::m_ops.find(this->id); if (it != matrix_operator::m_ops.end()) { - return it->second.generate_matrix(dimensions, parameters); + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->degrees.size()); + for (auto d : this->degrees) + relevant_dimensions.push_back(dimensions[d]); + return it->second.generate_matrix(relevant_dimensions, parameters); } throw std::range_error("unable to find operator"); } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index c50f3df218..b450f77e44 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -154,8 +154,7 @@ class operator_sum { std::tuple, std::vector> m_canonical_terms() const; - matrix_2 m_evaluate(MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; void aggregate_terms(); @@ -570,7 +569,7 @@ class matrix_operator { /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - static void define(std::string operator_id, std::map expected_dimensions, + static void define(std::string operator_id, std::vector expected_dimensions, CallbackFunction &&create) { auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index ede78fac53..5acde43b42 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -334,6 +334,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { /// Keeping this fixed throughout. int level_count = 3; + std::map dimensions = {{0, level_count}, {1, level_count}}; // Addition, same DOF. { @@ -343,7 +344,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self + other; ASSERT_TRUE(sum.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = utils_0::annihilate_matrix(level_count) + utils_0::create_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); @@ -362,9 +363,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - //auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = annihilate_full + create_full; - //utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Subtraction, same DOF. @@ -375,7 +376,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto sum = self - other; ASSERT_TRUE(sum.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = utils_0::annihilate_matrix(level_count) - utils_0::create_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); @@ -394,9 +395,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - //auto got_matrix = sum.to_matrix({{0, level_count}}); + auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = annihilate_full - create_full; - //utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } // Multiplication, same DOF. @@ -410,7 +411,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0, level_count}}); + auto got_matrix = product.to_matrix(dimensions); auto want_matrix = utils_0::annihilate_matrix(level_count) * utils_0::create_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); @@ -433,9 +434,9 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { utils_0::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), utils_0::id_matrix(level_count)); - //auto got_matrix = product.to_matrix({{0, level_count}}, {}); + auto got_matrix = product.to_matrix(dimensions); auto want_matrix = annihilate_full * create_full; - //utils_0::checkEqual(want_matrix, got_matrix); + utils_0::checkEqual(want_matrix, got_matrix); } } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 18e26be328..7d2fe8ee4e 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -194,8 +194,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - //auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -206,8 +206,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -225,8 +225,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - //auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); - //auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); From b118f8112d12b1c4f50783c5fc5c2f6046f70da6 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 16:00:03 +0000 Subject: [PATCH 195/311] just some test fixes Signed-off-by: Bettina Heim --- .../dynamics/product_operators_arithmetic.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 7d2fe8ee4e..c2a71618c8 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -234,8 +234,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector matrices_0; std::vector matrices_1; - matrices_0 = {identity, identity, matrix0}; - matrices_1 = {matrix1, identity, identity}; + matrices_0 = {identity, matrix0}; + matrices_1 = {matrix1, identity}; auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); @@ -244,8 +244,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(got_matrix, want_matrix); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(got_matrix, want_matrix); } } } @@ -660,8 +660,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= scalar_operator` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::momentum(1); auto scalar_op = cudaq::scalar_operator(value_0); product *= scalar_op; @@ -684,8 +684,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { value_0 * utils_1::id_matrix(level_count * level_count); auto want_matrix = product_matrix * scaled_identity; - - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } } From b35029529c75ade8bda480e56fd6d9a4a23eca42 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 22:12:02 +0000 Subject: [PATCH 196/311] fixing matrix reordering issues Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/helpers.cpp | 4 +- runtime/cudaq/dynamics/manipulation.cpp | 13 ++++--- runtime/cudaq/dynamics/operator_sum.cpp | 29 ++++----------- .../dynamics/product_operators_arithmetic.cpp | 37 +++++++++---------- 4 files changed, 35 insertions(+), 48 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index f8ab19f138..2eab4ec238 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -110,10 +110,10 @@ class _OperatorHelpers { states.push_back(std::to_string(state)); } - for (auto degree = degrees.begin() + 1; degree != degrees.end(); ++degree) { + for (auto idx = 1; idx < degrees.size(); ++idx) { std::vector result; for (auto current : states) { - for (auto state = 0; state < dimensions[degrees[*degree]]; state++) { + for (auto state = 0; state < dimensions[degrees[idx]]; state++) { result.push_back(current + std::to_string(state)); } } diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 1acbbbeb6c..5b713331a7 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -22,18 +22,21 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, auto it = std::find(canon_degrees.begin(), canon_degrees.end(), degree); reordering.push_back(it - canon_degrees.begin()); } - std::vector result; + + std::vector op_states = + cudaq::detail::generate_all_states(op_degrees, m_dimensions); + + std::vector permutation; for (auto state : states) { - int index; std::string term; for (auto i : reordering) { term += state[i]; } - auto it = std::find(states.begin(), states.end(), term); - result.push_back(it - states.begin()); + auto it = std::find(op_states.begin(), op_states.end(), term); + permutation.push_back(it - op_states.begin()); } - return result; + return permutation; } // Given a matrix representation that acts on the given degrees or freedom, diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 8fc83fdd32..858a4ec575 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -20,18 +20,10 @@ namespace cudaq { template cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const { + MatrixArithmetics arithmetics, bool pad_terms) const { auto terms = this->get_terms(); - - std::set degrees_set; - for (auto op : terms) { - for (auto degree : op.degrees()) { - degrees_set.insert(degree); - } - } - std::vector degrees(degrees_set.begin(), degrees_set.end()); + auto degrees = this->degrees(); // We need to make sure all matrices are of the same size to sum them up. auto paddedTerm = [&](auto &&term) { @@ -51,13 +43,11 @@ cudaq::matrix_2 operator_sum::m_evaluate( auto sum = EvaluatedMatrix(); if (pad_terms) { - - sum = EvaluatedMatrix(degrees, paddedTerm(terms[0]).m_evaluate(arithmetics, pad_terms)); + auto padded_term = paddedTerm(terms[0]); + sum = EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { - auto term = terms[term_idx]; - - auto eval = paddedTerm(term).m_evaluate(arithmetics, pad_terms); - sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + padded_term = paddedTerm(terms[term_idx]); + sum = arithmetics.add(sum, EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms))); } } else { sum = @@ -148,8 +138,7 @@ void operator_sum::aggregate_terms(const product_operator template cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, std::map dimensions, - std::map> parameters, bool pad_terms) const; + MatrixArithmetics arithmetics, bool pad_terms) const; template std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; @@ -301,9 +290,7 @@ std::string operator_sum::to_string() const { template matrix_2 operator_sum::to_matrix(const std::map &dimensions, const std::map> ¶meters) const { - /// FIXME: Not doing any conversion to spin op yet. - return m_evaluate(MatrixArithmetics(dimensions, parameters), dimensions, - parameters); + return m_evaluate(MatrixArithmetics(dimensions, parameters)); } template diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index c2a71618c8..b3cde934f6 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -691,6 +691,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; + std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; // `product_operator + product_operator` { @@ -706,10 +707,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); - //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}, {}); + auto got_matrix = sum.to_matrix(dimensions); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -732,11 +731,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix + term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` @@ -750,7 +749,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(difference.n_terms() == 2); - //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); + auto got_matrix = difference.to_matrix(dimensions); std::vector matrices_0_0; std::vector matrices_0_1; @@ -774,11 +773,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix - term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` @@ -792,7 +791,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(product.n_terms() == 4); - //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); + auto got_matrix = product.to_matrix(dimensions); std::vector matrices_0_0; std::vector matrices_0_1; @@ -816,11 +815,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` @@ -834,10 +833,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { ASSERT_TRUE(term_0.n_terms() == 4); - //auto got_matrix = term_0.to_matrix({{0,level_count},{1,level_count}, {2,level_count+1}}); + auto got_matrix = term_0.to_matrix(dimensions); - // Build up each individual term, cast to the full Hilbert space of the - // system. std::vector matrices_0_0; std::vector matrices_0_1; matrices_0_0 = {utils_1::id_matrix(level_count + 1), @@ -860,11 +857,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto term_1_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix, got_matrix); } } From 8d844c19b7ab1a7fe2f28dbf210281f4c8b1c0da Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 3 Feb 2025 23:49:40 +0000 Subject: [PATCH 197/311] enabling the rest of the tests Signed-off-by: Bettina Heim --- unittests/dynamics/operator_sum.cpp | 402 ++++++------------ .../dynamics/product_operators_arithmetic.cpp | 25 +- 2 files changed, 151 insertions(+), 276 deletions(-) diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index f3e649cc80..abbe10a52f 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -257,13 +257,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - /// Check the matrices. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = - // product.to_matrix({{1, level_count}, {2, level_count + 1}}); - // auto got_matrix_reverse = - // reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -275,8 +270,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - //utils_2::checkEqual(want_matrix, got_matrix); - //utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` @@ -291,26 +286,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, - // {2,level_count+1}}); - - // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - // utils_2::create_matrix(level_count)); - // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - // utils_2::id_matrix(level_count)); - // auto sum_matrix = matrix0 + matrix1; - // auto scaled_identity = - // value * utils_2::id_matrix((level_count) * (level_count + 1)); - - // auto want_matrix = sum_matrix + scaled_identity; - // auto want_matrix_reverse = scaled_identity + sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2,level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix_reverse = scaled_identity + sum_matrix; + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -324,26 +315,21 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = difference.to_matrix({{1, level_count}, {2, - // level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, - // level_count}, {2, level_count+1}}); - - // auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - // utils_2::create_matrix(level_count)); - // auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - // utils_2::id_matrix(level_count)); - // auto sum_matrix = matrix0 + matrix1; - // auto scaled_identity = - // value * utils_2::id_matrix((level_count) * (level_count + 1)); - - // auto want_matrix = sum_matrix - scaled_identity; - // auto want_matrix_reverse = scaled_identity - sum_matrix; - // // utils_2::checkEqual(want_matrix, got_matrix); - // // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), + utils_2::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils_2::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= scalar_operator` @@ -359,29 +345,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. - // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, - // {2, - // // level_count+1}}); - - // std::vector matrices_1 = { - // utils_2::id_matrix(level_count + 1), - // utils_2::create_matrix(level_count), - // utils_2::id_matrix(level_count)}; - // std::vector matrices_2 = { - // utils_2::momentum_matrix(level_count + 1), - // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; - // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - // auto scaled_identity = - // value * - // utils_2::id_matrix((level_count + 1) * level_count * level_count); - - // auto want_matrix = (matrix0 + matrix1) * scaled_identity; - // // utils_2::checkEqual(want_matrix, got_matrix); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + + std::vector matrices_1 = { + utils_2::id_matrix(level_count + 1), + utils_2::create_matrix(level_count)}; + std::vector matrices_2 = { + utils_2::momentum_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + + auto want_matrix = (matrix0 + matrix1) * scaled_identity; + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += scalar_operator` @@ -393,29 +370,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. - // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, - // {2, - // // level_count+1}}); - - // std::vector matrices_1 = { - // utils_2::id_matrix(level_count + 1), - // utils_2::parity_matrix(level_count), - // utils_2::id_matrix(level_count)}; - // std::vector matrices_2 = { - // utils_2::position_matrix(level_count + 1), - // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; - // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - // auto scaled_identity = - // value * - // utils_2::id_matrix((level_count + 1) * level_count * level_count); - - // auto want_matrix = matrix0 + matrix1 + scaled_identity; - // // utils_2::checkEqual(want_matrix, got_matrix); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + + std::vector matrices_1 = { + utils_2::id_matrix(level_count + 1), + utils_2::parity_matrix(level_count)}; + std::vector matrices_2 = { + utils_2::position_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + + auto want_matrix = matrix0 + matrix1 + scaled_identity; + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= scalar_operator` @@ -427,29 +395,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); - // /// Check the matrices. - // /// FIXME: Comment me back in when `to_matrix` is implemented. - - // // Providing dimensions for the `0`, `1` and `2` degrees of freedom. - // // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, - // {2, - // // level_count+1}}); - - // std::vector matrices_1 = { - // utils_2::id_matrix(level_count + 1), - // utils_2::number_matrix(level_count), - // utils_2::id_matrix(level_count)}; - // std::vector matrices_2 = { - // utils_2::annihilate_matrix(level_count + 1), - // utils_2::id_matrix(level_count), utils_2::id_matrix(level_count)}; - // auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - // auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - // auto scaled_identity = - // value * - // utils_2::id_matrix((level_count + 1) * level_count * level_count); - - // auto want_matrix = (matrix0 + matrix1) - scaled_identity; - // // utils_2::checkEqual(want_matrix, got_matrix); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + + std::vector matrices_1 = { + utils_2::id_matrix(level_count + 1), + utils_2::number_matrix(level_count)}; + std::vector matrices_2 = { + utils_2::annihilate_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + + auto want_matrix = (matrix0 + matrix1) - scaled_identity; + utils_2::checkEqual(want_matrix, got_matrix); } } @@ -479,14 +438,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = product.to_matrix({{1, level_count}, {2, - // level_count+1}}, - // {}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, - // level_count+1}}); + auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -498,8 +451,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + double` and `double + operator_sum` @@ -513,13 +466,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, - // level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -531,8 +479,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - double` and `double - operator_sum` @@ -546,13 +494,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, - // level_count+1}}); + auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::parity_matrix(level_count)); @@ -564,8 +507,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= double` @@ -581,12 +524,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -599,7 +537,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { double_value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += double` @@ -611,11 +549,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -627,7 +561,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { double_value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= double` @@ -639,11 +573,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -655,7 +585,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { double_value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * std::complex` and `std::complex * @@ -680,13 +610,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = product.to_matrix({{1,level_count}, {2, - // level_count+1}}); auto got_matrix_reverse = - // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = product.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -698,8 +623,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + std::complex` and `std::complex + @@ -714,13 +639,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); - // auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, - // level_count+1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -732,8 +652,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - std::complex` and `std::complex - @@ -748,13 +668,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = difference.to_matrix({{1,level_count}, {2, - // level_count+1}}); auto got_matrix_reverse = - // reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -766,8 +681,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= std::complex` @@ -783,12 +698,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {{"displacement", value}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"displacement", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -800,7 +710,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum += std::complex` @@ -812,12 +722,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -829,7 +734,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= std::complex` @@ -841,12 +746,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // Only providing dimensions for the `1` and `2` degrees of freedom. - // auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, - // {}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::position_matrix(level_count)); @@ -857,7 +757,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { value * utils_2::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } } @@ -876,11 +776,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 5); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, - // level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -918,7 +814,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix + sum_1_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum - operator_sum` @@ -933,11 +829,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 5); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = difference.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = difference.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -975,7 +867,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix - sum_1_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum * operator_sum` @@ -996,13 +888,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum_product_reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); auto - // got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -1041,8 +928,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto want_matrix = sum_0_matrix * sum_1_matrix; auto want_matrix_reverse = sum_1_matrix * sum_0_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); - // utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= operator_sum` @@ -1059,11 +946,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0,level_count}, {1, - // level_count+1}, {2, level_count+2}, {3, level_count+3}}, {}); + auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -1101,7 +984,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix * sum_1_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } } @@ -1122,11 +1005,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, - // {2, level_count+2}}, {}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1142,17 +1021,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix + product_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum -= product_operator` @@ -1166,11 +1046,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(sum.n_terms() == 3); - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, - // {2, level_count+2}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1186,17 +1062,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix - product_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } // `operator_sum *= product_operator` @@ -1213,11 +1090,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(term.n_terms() == 3); } - /// Check the matrices. - /// FIXME: Comment me back in when `to_matrix` is implemented. - - // auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, - // {2, level_count+2}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1233,16 +1106,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), + utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix * product_matrix; - // utils_2::checkEqual(want_matrix, got_matrix); + utils_2::checkEqual(want_matrix, got_matrix); } } \ No newline at end of file diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index b3cde934f6..cb4838fd6e 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -988,6 +988,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { int level_count = 3; + std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; // `product_operator + operator_sum` { @@ -1002,8 +1003,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - //auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + auto got_matrix = sum.to_matrix(dimensions); + auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1029,8 +1030,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix + sum_matrix; auto want_matrix_reverse = sum_matrix + product_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` @@ -1046,8 +1047,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - //auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + auto got_matrix = difference.to_matrix(dimensions); + auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1073,8 +1074,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix - difference_matrix; auto want_matrix_reverse = difference_matrix - product_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -1090,8 +1091,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - //auto got_matrix = product.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count+1}}); + auto got_matrix = product.to_matrix(dimensions); + auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. std::vector matrices_0_0 = { @@ -1117,7 +1118,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix * sum_matrix; auto want_matrix_reverse = sum_matrix * product_matrix; - //utils_1::checkEqual(want_matrix, got_matrix); - //utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils_1::checkEqual(want_matrix, got_matrix); + utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } From a4504adc235d8ce3fab2f6c6e0c5e46be898f4ba Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 09:11:01 -0800 Subject: [PATCH 198/311] Adding template parameter HandlerTy for operator_sum and formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 15 +- runtime/cudaq/cudm_helpers.h | 4 +- runtime/cudaq/definition.h | 21 +- runtime/cudaq/dynamics/definition.cpp | 12 +- runtime/cudaq/dynamics/helpers.cpp | 35 +- runtime/cudaq/dynamics/helpers.h | 32 +- runtime/cudaq/dynamics/manipulation.cpp | 11 +- runtime/cudaq/dynamics/matrix_operators.cpp | 34 +- runtime/cudaq/dynamics/operator_sum.cpp | 875 +++++++++--------- runtime/cudaq/dynamics/product_operators.cpp | 551 ++++++----- runtime/cudaq/dynamics/scalar_operators.cpp | 88 +- runtime/cudaq/dynamics/templates.h | 245 +++-- runtime/cudaq/operators.h | 388 ++++---- runtime/cudaq/utils/tensor.h | 2 +- unittests/dynamics/matrix_ops_arithmetic.cpp | 102 +- unittests/dynamics/matrix_ops_simple.cpp | 10 +- unittests/dynamics/operator_sum.cpp | 226 +++-- .../dynamics/product_operators_arithmetic.cpp | 167 ++-- 18 files changed, 1556 insertions(+), 1262 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 31d82ea201..1abe004c0d 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,7 +16,7 @@ #include namespace cudaq { -template +template class BaseIntegrator { protected: std::map integrator_options; @@ -24,9 +24,9 @@ class BaseIntegrator { double t; std::map dimensions; std::shared_ptr schedule; - std::shared_ptr hamiltonian; + std::shared_ptr> hamiltonian; std::shared_ptr> stepper; - std::vector> collapse_operators; + std::vector>> collapse_operators; virtual void post_init() = 0; @@ -61,10 +61,11 @@ class BaseIntegrator { } /// @brief Set the system parameters (dimensions, schedule, and operators) - void set_system( - const std::map &dimensions, std::shared_ptr schedule, - std::shared_ptr hamiltonian, - std::vector> collapse_operators = {}) { + void set_system(const std::map &dimensions, + std::shared_ptr schedule, + std::shared_ptr> hamiltonian, + std::vector>> + collapse_operators = {}) { this->dimensions = dimensions; this->schedule = schedule; this->hamiltonian = hamiltonian; diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 6ed2fc087b..36a1ba9688 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -25,10 +25,12 @@ compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents); +template cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, - const operator_sum &op, const std::vector &mode_extents); + const operator_sum &op, + const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/definition.h index 1421e92dc0..49decefeb6 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/definition.h @@ -25,8 +25,8 @@ using Func = std::function relevant_dimensions, std::map> parameters) const { - return _callback_func(std::move(relevant_dimensions), std::move(parameters)); + return _callback_func(std::move(relevant_dimensions), + std::move(parameters)); } }; @@ -114,7 +115,7 @@ class ScalarCallbackFunction : CallbackFunction { } // assignment operator - ScalarCallbackFunction& operator=(const ScalarCallbackFunction &other) { + ScalarCallbackFunction &operator=(const ScalarCallbackFunction &other) { if (this != &other) { _callback_func = other._callback_func; } @@ -122,7 +123,7 @@ class ScalarCallbackFunction : CallbackFunction { } // move assignment operator - ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other) { + ScalarCallbackFunction &operator=(ScalarCallbackFunction &&other) { if (this != &other) { _callback_func = std::move(other._callback_func); } @@ -141,14 +142,14 @@ class ScalarCallbackFunction : CallbackFunction { /// or scalar operator is instantiated by other means than the `define` /// class method. class Definition { -private: +private: std::string id; CallbackFunction generator; std::vector m_expected_dimensions; public: - - Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create); + Definition(const std::string &operator_id, + std::vector expected_dimensions, CallbackFunction &&create); Definition(Definition &&def); ~Definition(); diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp index c285a788ce..61e83d0a52 100644 --- a/runtime/cudaq/dynamics/definition.cpp +++ b/runtime/cudaq/dynamics/definition.cpp @@ -16,11 +16,15 @@ namespace cudaq { -Definition::Definition(const std::string &operator_id, std::vector expected_dimensions, CallbackFunction &&create) - : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} +Definition::Definition(const std::string &operator_id, + std::vector expected_dimensions, + CallbackFunction &&create) + : id(operator_id), generator(std::move(create)), + m_expected_dimensions(std::move(expected_dimensions)) {} -Definition::Definition(Definition &&def) - : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} +Definition::Definition(Definition &&def) + : id(def.id), generator(std::move(def.generator)), + m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( const std::vector &relevant_dimensions, diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 2eab4ec238..5a3d2f8390 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -6,12 +6,12 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/operators.h" -#include #include "cudaq/helpers.h" #include "cudaq/cudm_error_handling.h" +#include "cudaq/operators.h" #include #include +#include #include namespace cudaq { @@ -22,8 +22,9 @@ class _OperatorHelpers { _OperatorHelpers() = default; // Aggregate parameters from multiple mappings. - std::map aggregate_parameters( - const std::vector> ¶meter_mappings) { + std::map + aggregate_parameters(const std::vector> + ¶meter_mappings) { std::map parameter_descriptions; for (const auto &descriptions : parameter_mappings) { @@ -41,17 +42,17 @@ class _OperatorHelpers { // Extract documentation for a specific parameter from docstring. std::string parameter_docs(const std::string ¶m_name, - const std::string &docs) { + const std::string &docs) { if (param_name.empty() || docs.empty()) { return ""; } try { std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); + std::regex::multiline); std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); std::smatch match; std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); @@ -73,10 +74,9 @@ class _OperatorHelpers { // Extract positional arguments and keyword-only arguments. std::pair, std::map> - args_from_kwargs( - const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { + args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { std::vector extracted_args; std::map kwonly_dict; @@ -99,8 +99,8 @@ class _OperatorHelpers { /// Generates all possible states for the given dimensions ordered according /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector - generate_all_states(std::vector degrees, std::map dimensions) { + std::vector generate_all_states(std::vector degrees, + std::map dimensions) { if (degrees.size() == 0) return {}; @@ -124,7 +124,7 @@ class _OperatorHelpers { } cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation) { + std::vector permutation) { auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); std::vector> sorted_values; for (std::size_t permuted : permutation) { @@ -146,7 +146,6 @@ class _OperatorHelpers { std::sort(degrees.begin(), degrees.end(), std::greater()); return degrees; } - }; -} -} +} // namespace detail +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h index 5f336847a5..ec5446143e 100644 --- a/runtime/cudaq/dynamics/helpers.h +++ b/runtime/cudaq/dynamics/helpers.h @@ -6,27 +6,27 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/utils/tensor.h" #include #include -#include "cudaq/utils/tensor.h" namespace cudaq { namespace detail { - /// Generates all possible states for the given dimensions ordered according - /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector generate_all_states(std::vector degrees, std::map dimensions); - - // Permutes the given matrix according to the given permutation. - // If states is the current order of vector entries on which the given matrix - // acts, and permuted_states is the desired order of an array on which the - // permuted matrix should act, then the permutation is defined such that - // [states[i] for i in permutation] produces permuted_states. - cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation); +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector generate_all_states(std::vector degrees, + std::map dimensions); - // Returns the degrees sorted in canonical order. - std::vector canonicalize_degrees(std::vector degrees); -} -} +// Permutes the given matrix according to the given permutation. +// If states is the current order of vector entries on which the given matrix +// acts, and permuted_states is the desired order of an array on which the +// permuted matrix should act, then the permutation is defined such that +// [states[i] for i in permutation] produces permuted_states. +cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation); +// Returns the degrees sorted in canonical order. +std::vector canonicalize_degrees(std::vector degrees); +} // namespace detail +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 5b713331a7..e3d6f90447 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -14,8 +14,7 @@ namespace cudaq { std::vector MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::vector canon_degrees) { - auto states = - cudaq::detail::generate_all_states(canon_degrees, m_dimensions); + auto states = cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; for (auto degree : op_degrees) { @@ -23,7 +22,7 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, reordering.push_back(it - canon_degrees.begin()); } - std::vector op_states = + std::vector op_states = cudaq::detail::generate_all_states(op_degrees, m_dimensions); std::vector permutation; @@ -99,8 +98,10 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix + op2.m_matrix)); } -EvaluatedMatrix MatrixArithmetics::evaluate( - std::variant> op) { +EvaluatedMatrix +MatrixArithmetics::evaluate(std::variant> + op) { // auto getDegrees = [](auto &&t) { return t.degrees; }; // auto toMatrix = [&](auto &&t) { // return t.to_matrix(this->m_dimensions, this->m_parameters); diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index b8926ba470..d0b460e1ea 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -8,8 +8,8 @@ #include "cudaq/operators.h" -#include #include +#include #include namespace cudaq { @@ -20,7 +20,7 @@ product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); @@ -40,7 +40,7 @@ product_operator matrix_operator::zero(int degree) { std::string op_id = "zero"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { // Need to set the degree via the op itself because the // argument to the outer function goes out of scope when // the user invokes this later on via, e.g, `to_matrix()`. @@ -58,7 +58,7 @@ product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -76,7 +76,7 @@ product_operator matrix_operator::create(int degree) { std::string op_id = "create"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -94,7 +94,7 @@ product_operator matrix_operator::position(int degree) { std::string op_id = "position"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) @@ -116,7 +116,7 @@ product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) @@ -138,7 +138,7 @@ product_operator matrix_operator::number(int degree) { std::string op_id = "number"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -156,7 +156,7 @@ product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> _none) { + std::map> _none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -174,7 +174,7 @@ product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> parameters) { + std::map> parameters) { std::size_t dimension = dimensions[0]; auto displacement_amplitude = parameters["displacement"]; auto create = matrix_2(dimension, dimension); @@ -194,12 +194,11 @@ product_operator matrix_operator::displace(int degree) { return product_operator(1., op); } - product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, - std::map> parameters) { + std::map> parameters) { std::size_t dimension = dimensions[0]; auto squeezing = parameters["squeezing"]; auto create = matrix_2(dimension, dimension); @@ -220,17 +219,16 @@ product_operator matrix_operator::squeeze(int degree) { return product_operator(1., op); } - matrix_2 matrix_operator::to_matrix( std::map dimensions, std::map> parameters) const { auto it = matrix_operator::m_ops.find(this->id); if (it != matrix_operator::m_ops.end()) { - std::vector relevant_dimensions; - relevant_dimensions.reserve(this->degrees.size()); - for (auto d : this->degrees) - relevant_dimensions.push_back(dimensions[d]); - return it->second.generate_matrix(relevant_dimensions, parameters); + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->degrees.size()); + for (auto d : this->degrees) + relevant_dimensions.push_back(dimensions[d]); + return it->second.generate_matrix(relevant_dimensions, parameters); } throw std::range_error("unable to find operator"); } diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 858a4ec575..4f37bd536e 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -9,9 +9,9 @@ #include "cudaq/operators.h" #include "helpers.h" +#include #include #include -#include #include namespace cudaq { @@ -19,8 +19,9 @@ namespace cudaq { // private methods template -cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const { +cudaq::matrix_2 +operator_sum::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const { auto terms = this->get_terms(); auto degrees = this->degrees(); @@ -44,26 +45,29 @@ cudaq::matrix_2 operator_sum::m_evaluate( auto sum = EvaluatedMatrix(); if (pad_terms) { auto padded_term = paddedTerm(terms[0]); - sum = EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms)); + sum = EvaluatedMatrix(degrees, + padded_term.m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { padded_term = paddedTerm(terms[term_idx]); - sum = arithmetics.add(sum, EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms))); + sum = arithmetics.add( + sum, EvaluatedMatrix(degrees, + padded_term.m_evaluate(arithmetics, pad_terms))); } } else { - sum = - EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); + sum = EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; - auto eval = - term.m_evaluate(arithmetics, pad_terms); + auto eval = term.m_evaluate(arithmetics, pad_terms); sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); } } return sum.matrix(); } -template -std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const { +template +std::tuple, std::vector> +operator_sum::m_canonicalize_product( + product_operator &prod) const { std::vector scalars = {prod.get_coefficient()}; auto non_scalars = prod.get_terms(); @@ -109,8 +113,9 @@ std::tuple, std::vector> operator_sum -std::tuple, std::vector> operator_sum::m_canonical_terms() const { +template +std::tuple, std::vector> +operator_sum::m_canonical_terms() const { /// FIXME: Not doing the same sorting we do in python yet std::tuple, std::vector> result; std::vector scalars; @@ -120,195 +125,208 @@ std::tuple, std::vector> operator_sum(canon_term); auto canon_elementary = std::get<1>(canon_term); scalars.insert(scalars.end(), canon_scalars.begin(), canon_scalars.end()); - canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), canon_elementary.end()); + canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), + canon_elementary.end()); } return std::make_tuple(scalars, matrix_ops); } -template +template void operator_sum::aggregate_terms() {} -template -template -void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { - this->terms.push_back(head.terms[0]); - this->coefficients.push_back(head.coefficients[0]); - aggregate_terms(std::forward(args)...); +template +template +void operator_sum::aggregate_terms( + const product_operator &head, Args &&...args) { + this->terms.push_back(head.terms[0]); + this->coefficients.push_back(head.coefficients[0]); + aggregate_terms(std::forward(args)...); } -template -cudaq::matrix_2 operator_sum::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const; +template cudaq::matrix_2 +operator_sum::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const; -template -std::tuple, std::vector> operator_sum::m_canonicalize_product(product_operator &prod) const; +template std::tuple, std::vector> +operator_sum::m_canonicalize_product( + product_operator &prod) const; -template -std::tuple, std::vector> operator_sum::m_canonical_terms() const; +template std::tuple, std::vector> +operator_sum::m_canonical_terms() const; -// no overload for a single product, since we don't want a constructor for a single term +// no overload for a single product, since we don't want a constructor for a +// single term -template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2); +template void operator_sum::aggregate_terms( + const product_operator &item1, + const product_operator &item2); -template -void operator_sum::aggregate_terms(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +template void operator_sum::aggregate_terms( + const product_operator &item1, + const product_operator &item2, + const product_operator &item3); // read-only properties -template +template std::vector operator_sum::degrees() const { std::set unsorted_degrees; for (const std::vector &term : this->terms) { for (const HandlerTy &op : term) unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); } - auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + auto degrees = + std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); return cudaq::detail::canonicalize_degrees(degrees); } -template -int operator_sum::n_terms() const { - return this->terms.size(); +template +int operator_sum::n_terms() const { + return this->terms.size(); } -template -std::vector> operator_sum::get_terms() const { - std::vector> prods; - prods.reserve(this->terms.size()); - for (size_t i = 0; i < this->terms.size(); ++i) { - prods.push_back(product_operator(this->coefficients[i], this->terms[i])); - } - return prods; +template +std::vector> +operator_sum::get_terms() const { + std::vector> prods; + prods.reserve(this->terms.size()); + for (size_t i = 0; i < this->terms.size(); ++i) { + prods.push_back( + product_operator(this->coefficients[i], this->terms[i])); + } + return prods; } -template -std::vector operator_sum::degrees() const; +template std::vector operator_sum::degrees() const; -template -int operator_sum::n_terms() const; +template int operator_sum::n_terms() const; -template -std::vector> operator_sum::get_terms() const; +template std::vector> +operator_sum::get_terms() const; // constructors -template -template -operator_sum::operator_sum(const Args&... args) { - this->terms.reserve(sizeof...(Args)); - this->coefficients.reserve(sizeof...(Args)); - aggregate_terms(args...); +template +template +operator_sum::operator_sum(const Args &...args) { + this->terms.reserve(sizeof...(Args)); + this->coefficients.reserve(sizeof...(Args)); + aggregate_terms(args...); } -template -operator_sum::operator_sum(const std::vector> &terms) { - this->terms.reserve(terms.size()); - this->coefficients.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(term.terms[0]); - this->coefficients.push_back(term.coefficients[0]); - } +template +operator_sum::operator_sum( + const std::vector> &terms) { + this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); + for (const product_operator &term : terms) { + this->terms.push_back(term.terms[0]); + this->coefficients.push_back(term.coefficients[0]); + } } -template -operator_sum::operator_sum(std::vector> &&terms) { - this->terms.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(std::move(term.terms[0])); - this->coefficients.push_back(std::move(term.coefficients[0])); - } +template +operator_sum::operator_sum( + std::vector> &&terms) { + this->terms.reserve(terms.size()); + for (const product_operator &term : terms) { + this->terms.push_back(std::move(term.terms[0])); + this->coefficients.push_back(std::move(term.coefficients[0])); + } } -template +template operator_sum::operator_sum(const operator_sum &other) : coefficients(other.coefficients), terms(other.terms) {} -template -operator_sum::operator_sum(operator_sum &&other) - : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} +template +operator_sum::operator_sum(operator_sum &&other) + : coefficients(std::move(other.coefficients)), + terms(std::move(other.terms)) {} -// no constructor for a single product, since that one should remain a product op +// no constructor for a single product, since that one should remain a product +// op -template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2); +template operator_sum::operator_sum( + const product_operator &item1, + const product_operator &item2); -template -operator_sum::operator_sum(const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +template operator_sum::operator_sum( + const product_operator &item1, + const product_operator &item2, + const product_operator &item3); -template -operator_sum::operator_sum(const std::vector> &terms); +template operator_sum::operator_sum( + const std::vector> &terms); -template -operator_sum::operator_sum(std::vector> &&terms); +template operator_sum::operator_sum( + std::vector> &&terms); -template -operator_sum::operator_sum(const operator_sum &other); +template operator_sum::operator_sum( + const operator_sum &other); -template -operator_sum::operator_sum(operator_sum &&other); +template operator_sum::operator_sum( + operator_sum &&other); // assignments -template -operator_sum& operator_sum::operator=(const operator_sum &other) { - if (this != &other) { - coefficients = other.coefficients; - terms = other.terms; - } - return *this; +template +operator_sum & +operator_sum::operator=(const operator_sum &other) { + if (this != &other) { + coefficients = other.coefficients; + terms = other.terms; + } + return *this; } -template -operator_sum& operator_sum::operator=(operator_sum &&other) { - if (this != &other) { - coefficients = std::move(other.coefficients); - terms = std::move(other.terms); - } - return *this; +template +operator_sum & +operator_sum::operator=(operator_sum &&other) { + if (this != &other) { + coefficients = std::move(other.coefficients); + terms = std::move(other.terms); + } + return *this; } -template -operator_sum& operator_sum::operator=(const operator_sum& other); +template operator_sum & +operator_sum::operator=( + const operator_sum &other); -template -operator_sum& operator_sum::operator=(operator_sum &&other); +template operator_sum & +operator_sum::operator=(operator_sum &&other); // evaluations -template +template std::string operator_sum::to_string() const { - throw std::runtime_error("not implemented"); + throw std::runtime_error("not implemented"); } -template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map> ¶meters) const { +template +matrix_2 operator_sum::to_matrix( + const std::map &dimensions, + const std::map> ¶meters) const { return m_evaluate(MatrixArithmetics(dimensions, parameters)); } -template -std::string operator_sum::to_string() const; +template std::string operator_sum::to_string() const; -template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map> ¶ms) const; +template matrix_2 operator_sum::to_matrix( + const std::map &dimensions, + const std::map> ¶ms) const; // comparisons -template -bool operator_sum::operator==(const operator_sum &other) const { - throw std::runtime_error("not implemented"); +template +bool operator_sum::operator==( + const operator_sum &other) const { + throw std::runtime_error("not implemented"); } -template -bool operator_sum::operator==(const operator_sum &other) const; +template bool operator_sum::operator==( + const operator_sum &other) const; // unary operators @@ -329,48 +347,50 @@ operator_sum operator_sum::operator+() const { return *this; } -template -operator_sum operator_sum::operator-() const; +template operator_sum +operator_sum::operator-() const; -template -operator_sum operator_sum::operator+() const; +template operator_sum +operator_sum::operator+() const; // right-hand arithmetics -#define SUM_MULTIPLICATION(otherTy) \ - template \ - operator_sum operator_sum::operator*(otherTy other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size()); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff * other); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = this->terms; \ - return sum; \ +#define SUM_MULTIPLICATION(otherTy) \ + template \ + operator_sum operator_sum::operator*(otherTy other) \ + const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size()); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff *other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = this->terms; \ + return sum; \ } SUM_MULTIPLICATION(double); SUM_MULTIPLICATION(std::complex); SUM_MULTIPLICATION(const scalar_operator &); -#define SUM_ADDITION(otherTy, op) \ - template \ - operator_sum operator_sum::operator op(otherTy other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - coefficients.push_back(op other); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - terms.push_back({}); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION(otherTy, op) \ + template \ + operator_sum operator_sum::operator op(otherTy other) \ + const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op other); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + terms.push_back({}); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION(double, +); @@ -381,7 +401,8 @@ SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); template -operator_sum operator_sum::operator*(const HandlerTy &other) const { +operator_sum +operator_sum::operator*(const HandlerTy &other) const { std::vector> terms; terms.reserve(this->terms.size()); for (auto &term : this->terms) { @@ -398,58 +419,59 @@ operator_sum operator_sum::operator*(const HandlerTy &othe return sum; } -#define SUM_ADDITION_HANDLER(op) \ - template \ - operator_sum operator_sum::operator op( \ - const HandlerTy &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - coefficients.push_back(op 1.); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - terms.push_back(std::move(newTerm)); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_HANDLER(op) \ + template \ + operator_sum operator_sum::operator op( \ + const HandlerTy &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + coefficients.push_back(op 1.); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_HANDLER(+) SUM_ADDITION_HANDLER(-) -template -operator_sum operator_sum::operator*(double other) const; -template -operator_sum operator_sum::operator+(double other) const; -template -operator_sum operator_sum::operator-(double other) const; -template -operator_sum operator_sum::operator*(std::complex other) const; -template -operator_sum operator_sum::operator+(std::complex other) const; -template -operator_sum operator_sum::operator-(std::complex other) const; -template -operator_sum operator_sum::operator*(const scalar_operator &other) const; -template -operator_sum operator_sum::operator+(const scalar_operator &other) const; -template -operator_sum operator_sum::operator-(const scalar_operator &other) const; -template -operator_sum operator_sum::operator*(const matrix_operator &other) const; -template -operator_sum operator_sum::operator+(const matrix_operator &other) const; -template -operator_sum operator_sum::operator-(const matrix_operator &other) const; +template operator_sum +operator_sum::operator*(double other) const; +template operator_sum +operator_sum::operator+(double other) const; +template operator_sum +operator_sum::operator-(double other) const; +template operator_sum +operator_sum::operator*(std::complex other) const; +template operator_sum +operator_sum::operator+(std::complex other) const; +template operator_sum +operator_sum::operator-(std::complex other) const; +template operator_sum +operator_sum::operator*(const scalar_operator &other) const; +template operator_sum +operator_sum::operator+(const scalar_operator &other) const; +template operator_sum +operator_sum::operator-(const scalar_operator &other) const; +template operator_sum +operator_sum::operator*(const matrix_operator &other) const; +template operator_sum +operator_sum::operator+(const matrix_operator &other) const; +template operator_sum +operator_sum::operator-(const matrix_operator &other) const; template -operator_sum operator_sum::operator*(const product_operator &other) const { +operator_sum operator_sum::operator*( + const product_operator &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size()); for (auto &coeff : this->coefficients) @@ -459,7 +481,7 @@ operator_sum operator_sum::operator*(const product_operato for (auto &term : this->terms) { std::vector prod; prod.reserve(term.size() + other.terms[0].size()); - for (auto &op : term) + for (auto &op : term) prod.push_back(op); for (auto &op : other.terms[0]) prod.push_back(op); @@ -471,31 +493,32 @@ operator_sum operator_sum::operator*(const product_operato return sum; } -#define SUM_ADDITION_PRODUCT(op) \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - coefficients.push_back(op other.coefficients[0]); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - terms.push_back(other.terms[0]); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_PRODUCT(op) \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + coefficients.push_back(op other.coefficients[0]); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + terms.push_back(other.terms[0]); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum operator_sum::operator*(const operator_sum &other) const { +operator_sum +operator_sum::operator*(const operator_sum &other) const { std::vector coefficients; coefficients.reserve(this->coefficients.size() * other.coefficients.size()); for (auto &coeff1 : this->coefficients) { @@ -521,62 +544,65 @@ operator_sum operator_sum::operator*(const operator_sum \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_SUM(op) \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + \ + other.coefficients.size()); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + for (auto &coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + for (auto &term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); -template -operator_sum operator_sum::operator*(const product_operator &other) const; -template -operator_sum operator_sum::operator+(const product_operator &other) const; -template -operator_sum operator_sum::operator-(const product_operator &other) const; -template -operator_sum operator_sum::operator*(const operator_sum &other) const; -template -operator_sum operator_sum::operator+(const operator_sum &other) const; -template -operator_sum operator_sum::operator-(const operator_sum &other) const; - -#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - operator_sum& operator_sum::operator*=(otherTy other) { \ - for (auto &coeff : this->coefficients) \ - coeff *= other; \ - return *this; \ +template operator_sum operator_sum::operator*( + const product_operator &other) const; +template operator_sum operator_sum::operator+( + const product_operator &other) const; +template operator_sum operator_sum::operator-( + const product_operator &other) const; +template operator_sum operator_sum::operator*( + const operator_sum &other) const; +template operator_sum operator_sum::operator+( + const operator_sum &other) const; +template operator_sum operator_sum::operator-( + const operator_sum &other) const; + +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum &operator_sum::operator*=( \ + otherTy other) { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return *this; \ } SUM_MULTIPLICATION_ASSIGNMENT(double); SUM_MULTIPLICATION_ASSIGNMENT(std::complex); SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ - template \ - operator_sum& operator_sum::operator op##=(otherTy other) { \ - this->coefficients.push_back(op other); \ - this->terms.push_back({}); \ - return *this; \ +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + otherTy other) { \ + this->coefficients.push_back(op other); \ + this->terms.push_back({}); \ + return *this; \ } SUM_ADDITION_ASSIGNMENT(double, +); @@ -587,29 +613,31 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template -operator_sum& operator_sum::operator*=(const HandlerTy &other) { +operator_sum & +operator_sum::operator*=(const HandlerTy &other) { for (auto &term : this->terms) term.push_back(other); operator_sum sum; return *this; } -#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const HandlerTy &other) { \ - coefficients.push_back(op 1.); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - this->terms.push_back(std::move(newTerm)); \ - return *this; \ +#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const HandlerTy &other) { \ + coefficients.push_back(op 1.); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + this->terms.push_back(std::move(newTerm)); \ + return *this; \ } SUM_ADDITION_HANDLER_ASSIGNMENT(+) SUM_ADDITION_HANDLER_ASSIGNMENT(-) template -operator_sum& operator_sum::operator*=(const product_operator &other) { +operator_sum & +operator_sum::operator*=(const product_operator &other) { for (auto &coeff : this->coefficients) coeff *= other.coefficients[0]; for (auto &term : this->terms) { @@ -620,116 +648,125 @@ operator_sum& operator_sum::operator*=(const product_opera return *this; } -#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const product_operator &other) { \ - this->coefficients.push_back(op other.coefficients[0]); \ - this->terms.push_back(other.terms[0]); \ - return *this; \ +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const product_operator &other) { \ + this->coefficients.push_back(op other.coefficients[0]); \ + this->terms.push_back(other.terms[0]); \ + return *this; \ } SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template -operator_sum& operator_sum::operator*=(const operator_sum &other) { - this->coefficients.reserve(this->coefficients.size() * other.coefficients.size()); +operator_sum & +operator_sum::operator*=(const operator_sum &other) { + this->coefficients.reserve(this->coefficients.size() * + other.coefficients.size()); *this = *this * other; // we need to update all coefficients and terms anyway return *this; } -#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const operator_sum &other) { \ - this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - for (auto &coeff : other.coefficients) \ - this->coefficients.push_back(op coeff); \ - this->terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &term : other.terms) \ - this->terms.push_back(term); \ - return *this; \ +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const operator_sum &other) { \ + this->coefficients.reserve(this->coefficients.size() + \ + other.coefficients.size()); \ + for (auto &coeff : other.coefficients) \ + this->coefficients.push_back(op coeff); \ + this->terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : other.terms) \ + this->terms.push_back(term); \ + return *this; \ } SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); -template -operator_sum& operator_sum::operator*=(double other); -template -operator_sum& operator_sum::operator+=(double other); -template -operator_sum& operator_sum::operator-=(double other); -template -operator_sum& operator_sum::operator*=(std::complex other); -template -operator_sum& operator_sum::operator+=(std::complex other); -template -operator_sum& operator_sum::operator-=(std::complex other); -template -operator_sum& operator_sum::operator*=(const scalar_operator &other); -template -operator_sum& operator_sum::operator+=(const scalar_operator &other); -template -operator_sum& operator_sum::operator-=(const scalar_operator &other); -template -operator_sum& operator_sum::operator*=(const matrix_operator &other); -template -operator_sum& operator_sum::operator+=(const matrix_operator &other); -template -operator_sum& operator_sum::operator-=(const matrix_operator &other); -template -operator_sum& operator_sum::operator*=(const product_operator &other); -template -operator_sum& operator_sum::operator+=(const product_operator &other); -template -operator_sum& operator_sum::operator-=(const product_operator &other); -template -operator_sum& operator_sum::operator*=(const operator_sum &other); -template -operator_sum& operator_sum::operator-=(const operator_sum &other); -template -operator_sum& operator_sum::operator+=(const operator_sum &other); +template operator_sum & +operator_sum::operator*=(double other); +template operator_sum & +operator_sum::operator+=(double other); +template operator_sum & +operator_sum::operator-=(double other); +template operator_sum & +operator_sum::operator*=(std::complex other); +template operator_sum & +operator_sum::operator+=(std::complex other); +template operator_sum & +operator_sum::operator-=(std::complex other); +template operator_sum & +operator_sum::operator*=(const scalar_operator &other); +template operator_sum & +operator_sum::operator+=(const scalar_operator &other); +template operator_sum & +operator_sum::operator-=(const scalar_operator &other); +template operator_sum & +operator_sum::operator*=(const matrix_operator &other); +template operator_sum & +operator_sum::operator+=(const matrix_operator &other); +template operator_sum & +operator_sum::operator-=(const matrix_operator &other); +template operator_sum & +operator_sum::operator*=( + const product_operator &other); +template operator_sum & +operator_sum::operator+=( + const product_operator &other); +template operator_sum & +operator_sum::operator-=( + const product_operator &other); +template operator_sum & +operator_sum::operator*=( + const operator_sum &other); +template operator_sum & +operator_sum::operator-=( + const operator_sum &other); +template operator_sum & +operator_sum::operator+=( + const operator_sum &other); // left-hand arithmetics -#define SUM_MULTIPLICATION_REVERSE(otherTy) \ - template \ - operator_sum operator*(otherTy other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.coefficients.size()); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(coeff * other); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = self.terms; \ - return sum; \ +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.coefficients.size()); \ + for (auto &coeff : self.coefficients) \ + coefficients.push_back(coeff *other); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = self.terms; \ + return sum; \ } SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); -#define SUM_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.terms.size() + 1); \ - coefficients.push_back(other); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(self.terms.size() + 1); \ - terms.push_back({}); \ - for (auto &term : self.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(other); \ + for (auto &coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + terms.push_back({}); \ + for (auto &term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_REVERSE(double, +); @@ -740,7 +777,8 @@ SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); template -operator_sum operator*(const HandlerTy &other, const operator_sum &self) { +operator_sum operator*(const HandlerTy &other, + const operator_sum &self) { std::vector> terms; terms.reserve(self.terms.size()); for (auto &term : self.terms) { @@ -757,54 +795,63 @@ operator_sum operator*(const HandlerTy &other, const operator_sum \ - operator_sum operator op(const HandlerTy &other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.terms.size() + 1); \ - coefficients.push_back(1.); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(self.terms.size() + 1); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - terms.push_back(std::move(newTerm)); \ - for (auto &term : self.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy &other, \ + const operator_sum &self) { \ + std::vector coefficients; \ + coefficients.reserve(self.terms.size() + 1); \ + coefficients.push_back(1.); \ + for (auto &coeff : self.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(self.terms.size() + 1); \ + std::vector newTerm; \ + newTerm.push_back(other); \ + terms.push_back(std::move(newTerm)); \ + for (auto &term : self.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_HANDLER_REVERSE(+) SUM_ADDITION_HANDLER_REVERSE(-) -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator*(const matrix_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator+(const matrix_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator-(const matrix_operator &other, const operator_sum &self); +template operator_sum +operator*(const scalar_operator &other, + const operator_sum &self); +template operator_sum +operator*(std::complex other, + const operator_sum &self); +template operator_sum +operator*(double other, const operator_sum &self); +template operator_sum +operator*(const matrix_operator &other, + const operator_sum &self); +template operator_sum +operator+(const scalar_operator &other, + const operator_sum &self); +template operator_sum +operator+(double other, const operator_sum &self); +template operator_sum +operator+(std::complex other, + const operator_sum &self); +template operator_sum +operator+(const matrix_operator &other, + const operator_sum &self); +template operator_sum +operator-(const scalar_operator &other, + const operator_sum &self); +template operator_sum +operator-(double other, const operator_sum &self); +template operator_sum +operator-(std::complex other, + const operator_sum &self); +template operator_sum +operator-(const matrix_operator &other, + const operator_sum &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 0489eb9a4c..80ea64f6df 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -13,7 +13,6 @@ #include #include #include -#include namespace cudaq { @@ -26,37 +25,48 @@ _padded_op(MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, /// Creating the tensor product with op being last is most efficient. std::vector padded; for (const auto °ree : degrees) { - if (std::find(op.degrees.begin(), op.degrees.end(), degree) == op.degrees.end()) { - // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS ARE REQUIRED. - padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); - // FIXME: avoid creation of a product here - - // but we need to make sure identity is defined before using it (all ops are lazily defined...) - //padded.push_back(cudaq::matrix_operator("identity", {degree}).to_matrix()); + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == + op.degrees.end()) { + // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS + // ARE REQUIRED. + padded.push_back(EvaluatedMatrix( + {degree}, matrix_operator::identity(degree).to_matrix(dimensions))); + // FIXME: avoid creation of a product here - + // but we need to make sure identity is defined before using it (all ops + // are lazily defined...) + // padded.push_back(cudaq::matrix_operator("identity", + // {degree}).to_matrix()); } } matrix_2 mat = op.to_matrix(dimensions, parameters); auto res = EvaluatedMatrix(op.degrees, mat); // FIXME: PUT THIS LAST - for(auto &op : padded) + for (auto &op : padded) res = arithmetics.tensor(res, op); - return res; + return res; } -// FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - +// FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - // IT SUPPOSED TO TAKE A TRANSFORMATION (ANY OPERATOR ARITHMETICS) AND APPLY IT template -cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const { +cudaq::matrix_2 +product_operator::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const { const std::vector &terms = this->terms[0]; auto degrees = this->degrees(); cudaq::matrix_2 result; if (terms.size() > 0) { if (pad_terms) { - auto evaluated = _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + auto evaluated = + _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, + arithmetics.m_parameters); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { const HandlerTy &op = terms[op_idx]; - if (op.degrees.size() != 1 || op != cudaq::matrix_operator("identity", op.degrees)) { - auto padded = _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + if (op.degrees.size() != 1 || + op != cudaq::matrix_operator("identity", op.degrees)) { + auto padded = + _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, + arithmetics.m_parameters); evaluated = arithmetics.mul(evaluated, padded); } } @@ -65,8 +75,10 @@ cudaq::matrix_2 product_operator::m_evaluate( auto evaluated = arithmetics.evaluate(terms[0]); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { auto &op = terms[op_idx]; - auto mat = op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); - evaluated = arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + auto mat = + op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = + arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); } result = evaluated.matrix(); } @@ -79,73 +91,72 @@ cudaq::matrix_2 product_operator::m_evaluate( return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } -template +template void product_operator::aggregate_terms() {} -template -template -void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { +template +template +void product_operator::aggregate_terms(const HandlerTy &head, + Args &&...args) { this->terms[0].push_back(head); aggregate_terms(std::forward(args)...); } -template -void product_operator::aggregate_terms(const matrix_operator &item1, - const matrix_operator &item2); +template void product_operator::aggregate_terms( + const matrix_operator &item1, const matrix_operator &item2); -template -void product_operator::aggregate_terms(const matrix_operator &item1, - const matrix_operator &item2, - const matrix_operator &item3); +template void product_operator::aggregate_terms( + const matrix_operator &item1, const matrix_operator &item2, + const matrix_operator &item3); // read-only properties -template +template std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->terms[0]) { unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); } - auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); + auto degrees = + std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); return cudaq::detail::canonicalize_degrees(degrees); } -template -int product_operator::n_terms() const { - return this->terms[0].size(); +template +int product_operator::n_terms() const { + return this->terms[0].size(); } -template -std::vector product_operator::get_terms() const { - return this->terms[0]; +template +std::vector product_operator::get_terms() const { + return this->terms[0]; } -template -scalar_operator product_operator::get_coefficient() const { - return this->coefficients[0]; +template +scalar_operator product_operator::get_coefficient() const { + return this->coefficients[0]; } -template -cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const; +template cudaq::matrix_2 +product_operator::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const; -template -std::vector product_operator::degrees() const; +template std::vector product_operator::degrees() const; -template -int product_operator::n_terms() const; +template int product_operator::n_terms() const; -template -std::vector product_operator::get_terms() const; +template std::vector +product_operator::get_terms() const; -template -scalar_operator product_operator::get_coefficient() const; +template scalar_operator +product_operator::get_coefficient() const; // constructors -template -template -product_operator::product_operator(scalar_operator coefficient, const Args&... args) { +template +template +product_operator::product_operator(scalar_operator coefficient, + const Args &...args) { this->coefficients.push_back(std::move(coefficient)); std::vector ops = {}; ops.reserve(sizeof...(Args)); @@ -153,64 +164,68 @@ product_operator::product_operator(scalar_operator coefficient, const aggregate_terms(args...); } -template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) { +template +product_operator::product_operator( + scalar_operator coefficient, + const std::vector &atomic_operators) { this->terms.push_back(atomic_operators); this->coefficients.push_back(std::move(coefficient)); } -template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) { +template +product_operator::product_operator( + scalar_operator coefficient, std::vector &&atomic_operators) { this->terms.push_back(std::move(atomic_operators)); this->coefficients.push_back(std::move(coefficient)); } -template -product_operator::product_operator(const product_operator &other) { +template +product_operator::product_operator( + const product_operator &other) { this->terms = other.terms; this->coefficients = other.coefficients; } -template -product_operator::product_operator(product_operator &&other) { +template +product_operator::product_operator( + product_operator &&other) { this->terms = std::move(other.terms); this->coefficients = std::move(other.coefficients); } -template -product_operator::product_operator(scalar_operator coefficient); +template product_operator::product_operator( + scalar_operator coefficient); -template -product_operator::product_operator(scalar_operator coefficient, - const matrix_operator &item1); +template product_operator::product_operator( + scalar_operator coefficient, const matrix_operator &item1); -template -product_operator::product_operator(scalar_operator coefficient, - const matrix_operator &item1, - const matrix_operator &item2); +template product_operator::product_operator( + scalar_operator coefficient, const matrix_operator &item1, + const matrix_operator &item2); -template -product_operator::product_operator(scalar_operator coefficient, - const matrix_operator &item1, - const matrix_operator &item2, - const matrix_operator &item3); +template product_operator::product_operator( + scalar_operator coefficient, const matrix_operator &item1, + const matrix_operator &item2, const matrix_operator &item3); -template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators); +template product_operator::product_operator( + scalar_operator coefficient, + const std::vector &atomic_operators); -template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators); +template product_operator::product_operator( + scalar_operator coefficient, + std::vector &&atomic_operators); -template -product_operator::product_operator(const product_operator &other); +template product_operator::product_operator( + const product_operator &other); -template -product_operator::product_operator(product_operator &&other); +template product_operator::product_operator( + product_operator &&other); // assignments -template -product_operator& product_operator::operator=(const product_operator &other) { +template +product_operator &product_operator::operator=( + const product_operator &other) { if (this != &other) { this->terms = other.terms; this->coefficients = other.coefficients; @@ -218,8 +233,9 @@ product_operator& product_operator::operator=(const produc return *this; } -template -product_operator& product_operator::operator=(product_operator &&other) { +template +product_operator & +product_operator::operator=(product_operator &&other) { if (this != &other) { this->coefficients = std::move(other.coefficients); this->terms = std::move(other.terms); @@ -227,47 +243,51 @@ product_operator& product_operator::operator=(product_oper return *this; } -template -product_operator& product_operator::operator=(const product_operator &other); +template product_operator & +product_operator::operator=( + const product_operator &other); -template -product_operator& product_operator::operator=(product_operator &&other); +template product_operator & +product_operator::operator=( + product_operator &&other); // evaluations -template +template std::string product_operator::to_string() const { throw std::runtime_error("not implemented"); } -template -matrix_2 product_operator::to_matrix(std::map dimensions, - std::map> parameters) const { +template +matrix_2 product_operator::to_matrix( + std::map dimensions, + std::map> parameters) const { return this->m_evaluate(MatrixArithmetics(dimensions, parameters)); } -template -std::string product_operator::to_string() const; +template std::string product_operator::to_string() const; -template -matrix_2 product_operator::to_matrix(std::map dimensions, - std::map> parameters) const; +template matrix_2 product_operator::to_matrix( + std::map dimensions, + std::map> parameters) const; // comparisons -template -bool product_operator::operator==(const product_operator &other) const { +template +bool product_operator::operator==( + const product_operator &other) const { throw std::runtime_error("not implemented"); } -template -bool product_operator::operator==(const product_operator &other) const; +template bool product_operator::operator==( + const product_operator &other) const; // unary operators template product_operator product_operator::operator-() const { - return product_operator(-1. * this->coefficients[0], this->terms[0]); + return product_operator(-1. * this->coefficients[0], + this->terms[0]); } template @@ -275,30 +295,32 @@ product_operator product_operator::operator+() const { return *this; } -template -product_operator product_operator::operator-() const; +template product_operator +product_operator::operator-() const; -template -product_operator product_operator::operator+() const; +template product_operator +product_operator::operator+() const; // right-hand arithmetics -#define PRODUCT_MULTIPLICATION(otherTy) \ - template \ - product_operator product_operator::operator*( \ - otherTy other) const { \ - return product_operator(other * this->coefficients[0], this->terms[0]); \ +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficients[0], \ + this->terms[0]); \ } PRODUCT_MULTIPLICATION(double); PRODUCT_MULTIPLICATION(std::complex); PRODUCT_MULTIPLICATION(const scalar_operator &); -#define PRODUCT_ADDITION(otherTy, op) \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) const { \ - return operator_sum(product_operator(op other), *this); \ +#define PRODUCT_ADDITION(otherTy, op) \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const { \ + return operator_sum(product_operator(op other), \ + *this); \ } PRODUCT_ADDITION(double, +); @@ -309,7 +331,8 @@ PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); template -product_operator product_operator::operator*(const HandlerTy &other) const { +product_operator +product_operator::operator*(const HandlerTy &other) const { std::vector terms; terms.reserve(this->terms[0].size() + 1); for (auto &term : this->terms[0]) @@ -318,64 +341,74 @@ product_operator product_operator::operator*(const Handler return product_operator(this->coefficients[0], std::move(terms)); } -#define PRODUCT_ADDITION_HANDLER(op) \ - template \ - operator_sum product_operator::operator op( \ - const HandlerTy &other) const { \ - return operator_sum(product_operator(op 1., other), *this); \ +#define PRODUCT_ADDITION_HANDLER(op) \ + template \ + operator_sum product_operator::operator op( \ + const HandlerTy &other) const { \ + return operator_sum(product_operator(op 1., other), \ + *this); \ } PRODUCT_ADDITION_HANDLER(+) PRODUCT_ADDITION_HANDLER(-) -template -product_operator product_operator::operator*(double other) const; -template -operator_sum product_operator::operator+(double other) const; -template -operator_sum product_operator::operator-(double other) const; -template -product_operator product_operator::operator*(std::complex other) const; -template -operator_sum product_operator::operator+(std::complex other) const; -template -operator_sum product_operator::operator-(std::complex other) const; -template -product_operator product_operator::operator*(const scalar_operator &other) const; -template -operator_sum product_operator::operator+(const scalar_operator &other) const; -template -operator_sum product_operator::operator-(const scalar_operator &other) const; -template -product_operator product_operator::operator*(const matrix_operator &other) const; -template -operator_sum product_operator::operator+(const matrix_operator &other) const; -template -operator_sum product_operator::operator-(const matrix_operator &other) const; +template product_operator +product_operator::operator*(double other) const; +template operator_sum +product_operator::operator+(double other) const; +template operator_sum +product_operator::operator-(double other) const; +template product_operator +product_operator::operator*(std::complex other) const; +template operator_sum +product_operator::operator+(std::complex other) const; +template operator_sum +product_operator::operator-(std::complex other) const; +template product_operator +product_operator::operator*( + const scalar_operator &other) const; +template operator_sum +product_operator::operator+( + const scalar_operator &other) const; +template operator_sum +product_operator::operator-( + const scalar_operator &other) const; +template product_operator +product_operator::operator*( + const matrix_operator &other) const; +template operator_sum +product_operator::operator+( + const matrix_operator &other) const; +template operator_sum +product_operator::operator-( + const matrix_operator &other) const; template -product_operator product_operator::operator*(const product_operator &other) const { +product_operator product_operator::operator*( + const product_operator &other) const { std::vector terms; terms.reserve(this->terms[0].size() + other.terms[0].size()); for (auto &term : this->terms[0]) terms.push_back(term); for (auto &term : other.terms[0]) terms.push_back(term); - return product_operator(this->coefficients[0] * other.coefficients[0], std::move(terms)); + return product_operator(this->coefficients[0] * other.coefficients[0], + std::move(terms)); } -#define PRODUCT_ADDITION_PRODUCT(op) \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) const { \ - return operator_sum(op other, *this); \ +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(op other, *this); \ } PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) template -operator_sum product_operator::operator*(const operator_sum &other) const { +operator_sum product_operator::operator*( + const operator_sum &other) const { std::vector coefficients; coefficients.reserve(other.coefficients.size()); for (auto &coeff : other.coefficients) @@ -387,7 +420,7 @@ operator_sum product_operator::operator*(const operator_su prod.reserve(this->terms[0].size() + term.size()); for (auto &op : this->terms[0]) prod.push_back(op); - for (auto &op : term) + for (auto &op : term) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -397,47 +430,54 @@ operator_sum product_operator::operator*(const operator_su return sum; } -#define PRODUCT_ADDITION_SUM(op) \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(other.coefficients.size() + 1); \ - coefficients.push_back(this->coefficients[0]); \ - for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(other.terms.size() + 1); \ - terms.push_back(this->terms[0]); \ - for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(other.coefficients.size() + 1); \ + coefficients.push_back(this->coefficients[0]); \ + for (auto &coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(other.terms.size() + 1); \ + terms.push_back(this->terms[0]); \ + for (auto &term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) -template -product_operator product_operator::operator*(const product_operator &other) const; -template -operator_sum product_operator::operator+(const product_operator &other) const; -template -operator_sum product_operator::operator-(const product_operator &other) const; -template -operator_sum product_operator::operator*(const operator_sum &other) const; -template -operator_sum product_operator::operator+(const operator_sum &other) const; -template -operator_sum product_operator::operator-(const operator_sum &other) const; - -#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - product_operator& product_operator::operator*=(otherTy other) { \ - this->coefficients[0] *= other; \ - return *this; \ +template product_operator +product_operator::operator*( + const product_operator &other) const; +template operator_sum +product_operator::operator+( + const product_operator &other) const; +template operator_sum +product_operator::operator-( + const product_operator &other) const; +template operator_sum +product_operator::operator*( + const operator_sum &other) const; +template operator_sum +product_operator::operator+( + const operator_sum &other) const; +template operator_sum +product_operator::operator-( + const operator_sum &other) const; + +#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + product_operator &product_operator::operator*=( \ + otherTy other) { \ + this->coefficients[0] *= other; \ + return *this; \ } PRODUCT_MULTIPLICATION_ASSIGNMENT(double); @@ -445,48 +485,54 @@ PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); template -product_operator& product_operator::operator*=(const HandlerTy &other) { +product_operator & +product_operator::operator*=(const HandlerTy &other) { this->terms[0].push_back(other); return *this; } template -product_operator& product_operator::operator*=(const product_operator &other) { +product_operator &product_operator::operator*=( + const product_operator &other) { this->coefficients[0] *= other.coefficients[0]; this->terms[0].reserve(this->terms[0].size() + other.terms[0].size()); - this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), other.terms[0].end()); + this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), + other.terms[0].end()); return *this; } -template -product_operator& product_operator::operator*=(double other); -template -product_operator& product_operator::operator*=(std::complex other); -template -product_operator& product_operator::operator*=(const scalar_operator &other); -template -product_operator& product_operator::operator*=(const matrix_operator &other); -template -product_operator& product_operator::operator*=(const product_operator &other); +template product_operator & +product_operator::operator*=(double other); +template product_operator & +product_operator::operator*=(std::complex other); +template product_operator & +product_operator::operator*=(const scalar_operator &other); +template product_operator & +product_operator::operator*=(const matrix_operator &other); +template product_operator & +product_operator::operator*=( + const product_operator &other); // left-hand arithmetics -#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ - template \ - product_operator operator*(otherTy other, \ - const product_operator &self) { \ - return product_operator(other * self.coefficients[0], self.terms[0]); \ +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*( \ + otherTy other, const product_operator &self) { \ + return product_operator(other * self.coefficients[0], \ + self.terms[0]); \ } PRODUCT_MULTIPLICATION_REVERSE(double); PRODUCT_MULTIPLICATION_REVERSE(std::complex); PRODUCT_MULTIPLICATION_REVERSE(const scalar_operator &); -#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const product_operator &self) { \ - return operator_sum(product_operator(other), op self); \ +#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op( \ + otherTy other, const product_operator &self) { \ + return operator_sum(product_operator(other), \ + op self); \ } PRODUCT_ADDITION_REVERSE(double, +); @@ -497,7 +543,8 @@ PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); template -product_operator operator*(const HandlerTy &other, const product_operator &self) { +product_operator operator*(const HandlerTy &other, + const product_operator &self) { std::vector terms; terms.reserve(self.terms[0].size() + 1); terms.push_back(other); @@ -506,39 +553,49 @@ product_operator operator*(const HandlerTy &other, const product_oper return product_operator(self.coefficients[0], std::move(terms)); } -#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ - template \ - operator_sum operator op(const HandlerTy &other, \ - const product_operator &self) { \ - return operator_sum(product_operator(1., other), op self); \ +#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op( \ + const HandlerTy &other, const product_operator &self) { \ + return operator_sum(product_operator(1., other), \ + op self); \ } PRODUCT_ADDITION_HANDLER_REVERSE(+) PRODUCT_ADDITION_HANDLER_REVERSE(-) -template -product_operator operator*(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(const matrix_operator &other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(const matrix_operator &other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(const matrix_operator &other, const product_operator &self); +template product_operator +operator*(double other, const product_operator &self); +template product_operator +operator*(std::complex other, + const product_operator &self); +template product_operator +operator*(const scalar_operator &other, + const product_operator &self); +template product_operator +operator*(const matrix_operator &other, + const product_operator &self); +template operator_sum +operator+(double other, const product_operator &self); +template operator_sum +operator+(std::complex other, + const product_operator &self); +template operator_sum +operator+(const scalar_operator &other, + const product_operator &self); +template operator_sum +operator+(const matrix_operator &other, + const product_operator &self); +template operator_sum +operator-(double other, const product_operator &self); +template operator_sum +operator-(std::complex other, + const product_operator &self); +template operator_sum +operator-(const scalar_operator &other, + const product_operator &self); +template operator_sum +operator-(const matrix_operator &other, + const product_operator &self); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index b933ad0783..5371926a07 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -15,31 +15,31 @@ namespace cudaq { // constructors and destructors -scalar_operator::scalar_operator(double value) - : constant_value(value), generator() {} +scalar_operator::scalar_operator(double value) + : constant_value(value), generator() {} -scalar_operator::scalar_operator(std::complex value) - : constant_value(value), generator() {} +scalar_operator::scalar_operator(std::complex value) + : constant_value(value), generator() {} -scalar_operator::scalar_operator(const ScalarCallbackFunction &create) - : constant_value(), generator(create) {} +scalar_operator::scalar_operator(const ScalarCallbackFunction &create) + : constant_value(), generator(create) {} scalar_operator::scalar_operator(ScalarCallbackFunction &&create) - : constant_value() { - generator = std::move(create); + : constant_value() { + generator = std::move(create); } -scalar_operator::scalar_operator(const scalar_operator &other) - : constant_value(other.constant_value), generator(other.generator) {} +scalar_operator::scalar_operator(const scalar_operator &other) + : constant_value(other.constant_value), generator(other.generator) {} -scalar_operator::scalar_operator(scalar_operator &&other) - : constant_value(other.constant_value) { - generator = std::move(other.generator); +scalar_operator::scalar_operator(scalar_operator &&other) + : constant_value(other.constant_value) { + generator = std::move(other.generator); } // assignments -scalar_operator& scalar_operator::operator=(const scalar_operator &other) { +scalar_operator &scalar_operator::operator=(const scalar_operator &other) { if (this != &other) { constant_value = other.constant_value; generator = other.generator; @@ -47,7 +47,7 @@ scalar_operator& scalar_operator::operator=(const scalar_operator &other) { return *this; } -scalar_operator& scalar_operator::operator=(scalar_operator &&other) { +scalar_operator &scalar_operator::operator=(scalar_operator &&other) { if (this != &other) { constant_value = other.constant_value; generator = std::move(other.generator); @@ -59,8 +59,10 @@ scalar_operator& scalar_operator::operator=(scalar_operator &&other) { std::complex scalar_operator::evaluate( const std::map> parameters) const { - if (constant_value.has_value()) return constant_value.value(); - else return generator(parameters); + if (constant_value.has_value()) + return constant_value.value(); + else + return generator(parameters); } matrix_2 scalar_operator::to_matrix( @@ -83,13 +85,9 @@ bool scalar_operator::operator==(scalar_operator other) { // unary operators -scalar_operator scalar_operator::operator-() const { - return *this * (-1.); -} +scalar_operator scalar_operator::operator-() const { return *this * (-1.); } -scalar_operator scalar_operator::operator+() const { - return *this; -} +scalar_operator scalar_operator::operator+() const { return *this; } // right-hand arithmetics @@ -99,10 +97,10 @@ scalar_operator scalar_operator::operator+() const { return scalar_operator(this->constant_value.value() op other); \ } \ auto newGenerator = \ - [other, generator = this->generator]( \ - std::map> parameters) { \ - return generator(parameters) op other; \ - }; \ + [other, generator = this->generator]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ return scalar_operator(newGenerator); \ } @@ -116,16 +114,16 @@ ARITHMETIC_OPERATIONS(+, std::complex); ARITHMETIC_OPERATIONS(-, std::complex); #define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op( \ - const scalar_operator &other) const { \ + scalar_operator scalar_operator::operator op(const scalar_operator &other) \ + const { \ if (this->constant_value.has_value() && \ other.constant_value.has_value()) { \ auto res = this->constant_value.value() op other.constant_value.value(); \ return scalar_operator(res); \ } \ auto newGenerator = \ - [other, *this]( \ - std::map> parameters) { \ + [other, \ + *this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ return scalar_operator(newGenerator); \ @@ -137,16 +135,16 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); #define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ - scalar_operator& scalar_operator::operator op(otherTy other) { \ + scalar_operator &scalar_operator::operator op(otherTy other) { \ if (this->constant_value.has_value()) { \ this->constant_value.value() op other; \ return *this; \ } \ auto newGenerator = \ - [other, generator = std::move(this->generator)]( \ - std::map> parameters) { \ - return generator(parameters) op other; \ - }; \ + [other, generator = std::move(this->generator)]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ this->generator = newGenerator; \ return *this; \ } @@ -161,16 +159,16 @@ ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, std::complex); ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, std::complex); #define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op( \ - const scalar_operator &other) { \ + scalar_operator &scalar_operator::operator op( \ + const scalar_operator &other) { \ if (this->constant_value.has_value() && \ other.constant_value.has_value()) { \ this->constant_value.value() op other.constant_value.value(); \ return *this; \ } \ auto newGenerator = \ - [other, *this]( \ - std::map> parameters) { \ + [other, \ + *this](std::map> parameters) { \ return this->evaluate(parameters) op other.evaluate(parameters); \ }; \ this->generator = newGenerator; \ @@ -184,7 +182,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); #define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ scalar_operator operator op(scalar_operator &&self, otherTy other) { \ - return std::move(self op##= other); \ + return std::move(self op## = other); \ } ARITHMETIC_OPERATIONS_RVALUE(*, double); @@ -204,10 +202,10 @@ ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); return scalar_operator(other op self.constant_value.value()); \ } \ auto newGenerator = \ - [other, generator = self.generator]( \ - std::map> parameters) { \ - return other op generator(parameters); \ - }; \ + [other, generator = self.generator]( \ + std::map> parameters) { \ + return other op generator(parameters); \ + }; \ return scalar_operator(newGenerator); \ } diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 2a999beb8c..ecf1b7241d 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -6,9 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -//#include -#include +// #include #include +#include namespace cudaq { @@ -16,113 +16,154 @@ class scalar_operator; class matrix_operator; -template +template class product_operator; -template +template class operator_sum; -template -product_operator operator*(double other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -operator_sum operator-(const HandlerTy &other, const product_operator &self); - -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self); +template +product_operator operator*(double other, + const product_operator &self); +template +operator_sum operator+(double other, + const product_operator &self); +template +operator_sum operator-(double other, + const product_operator &self); +template +product_operator operator*(std::complex other, + const product_operator &self); +template +operator_sum operator+(std::complex other, + const product_operator &self); +template +operator_sum operator-(std::complex other, + const product_operator &self); +template +product_operator operator*(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, + const product_operator &self); +template +product_operator operator*(const HandlerTy &other, + const product_operator &self); +template +operator_sum operator+(const HandlerTy &other, + const product_operator &self); +template +operator_sum operator-(const HandlerTy &other, + const product_operator &self); +template +operator_sum operator*(double other, + const operator_sum &self); +template +operator_sum operator+(double other, + const operator_sum &self); +template +operator_sum operator-(double other, + const operator_sum &self); +template +operator_sum operator*(std::complex other, + const operator_sum &self); +template +operator_sum operator+(std::complex other, + const operator_sum &self); +template +operator_sum operator-(std::complex other, + const operator_sum &self); +template +operator_sum operator*(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator*(const HandlerTy &other, + const operator_sum &self); +template +operator_sum operator+(const HandlerTy &other, + const operator_sum &self); +template +operator_sum operator-(const HandlerTy &other, + const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES -extern template -product_operator operator*(double other, const product_operator &self); -extern template -operator_sum operator+(double other, const product_operator &self); -extern template -operator_sum operator-(double other, const product_operator &self); -extern template -product_operator operator*(std::complex other, const product_operator &self); -extern template -operator_sum operator+(std::complex other, const product_operator &self); -extern template -operator_sum operator-(std::complex other, const product_operator &self); -extern template -product_operator operator*(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -extern template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -extern template -product_operator operator*(const matrix_operator &other, const product_operator &self); -extern template -operator_sum operator+(const matrix_operator &other, const product_operator &self); -extern template -operator_sum operator-(const matrix_operator &other, const product_operator &self); +extern template product_operator +operator*(double other, const product_operator &self); +extern template operator_sum +operator+(double other, const product_operator &self); +extern template operator_sum +operator-(double other, const product_operator &self); +extern template product_operator +operator*(std::complex other, + const product_operator &self); +extern template operator_sum +operator+(std::complex other, + const product_operator &self); +extern template operator_sum +operator-(std::complex other, + const product_operator &self); +extern template product_operator +operator*(const scalar_operator &other, + const product_operator &self); +extern template operator_sum +operator+(const scalar_operator &other, + const product_operator &self); +extern template operator_sum +operator-(const scalar_operator &other, + const product_operator &self); +extern template product_operator +operator*(const matrix_operator &other, + const product_operator &self); +extern template operator_sum +operator+(const matrix_operator &other, + const product_operator &self); +extern template operator_sum +operator-(const matrix_operator &other, + const product_operator &self); -extern template -operator_sum operator*(double other, const operator_sum &self); -extern template -operator_sum operator+(double other, const operator_sum &self); -extern template -operator_sum operator-(double other, const operator_sum &self); -extern template -operator_sum operator*(std::complex other, const operator_sum &self); -extern template -operator_sum operator+(std::complex other, const operator_sum &self); -extern template -operator_sum operator-(std::complex other, const operator_sum &self); -extern template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -extern template -operator_sum operator*(const matrix_operator &other, const operator_sum &self); -extern template -operator_sum operator+(const matrix_operator &other, const operator_sum &self); -extern template -operator_sum operator-(const matrix_operator &other, const operator_sum &self); +extern template operator_sum +operator*(double other, const operator_sum &self); +extern template operator_sum +operator+(double other, const operator_sum &self); +extern template operator_sum +operator-(double other, const operator_sum &self); +extern template operator_sum +operator*(std::complex other, + const operator_sum &self); +extern template operator_sum +operator+(std::complex other, + const operator_sum &self); +extern template operator_sum +operator-(std::complex other, + const operator_sum &self); +extern template operator_sum +operator*(const scalar_operator &other, + const operator_sum &self); +extern template operator_sum +operator+(const scalar_operator &other, + const operator_sum &self); +extern template operator_sum +operator-(const scalar_operator &other, + const operator_sum &self); +extern template operator_sum +operator*(const matrix_operator &other, + const operator_sum &self); +extern template operator_sum +operator+(const matrix_operator &other, + const operator_sum &self); +extern template operator_sum +operator-(const matrix_operator &other, + const operator_sum &self); #endif -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index b450f77e44..2a9281ace9 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,22 +8,21 @@ #pragma once -#include "dynamics/templates.h" #include "definition.h" +#include "dynamics/templates.h" #include "utils/tensor.h" +#include #include #include #include #include -#include #include namespace cudaq { class MatrixArithmetics; - class scalar_operator { private: @@ -37,7 +36,6 @@ class scalar_operator { ScalarCallbackFunction generator; public: - // constructors and destructors scalar_operator(double value); @@ -64,21 +62,22 @@ class scalar_operator { // assignments // assignment operator - scalar_operator& operator=(const scalar_operator &other); + scalar_operator &operator=(const scalar_operator &other); // move assignment operator - scalar_operator& operator=(scalar_operator &&other); + scalar_operator &operator=(scalar_operator &&other); // evaluations /// @brief Return the scalar operator as a concrete complex value. - std::complex - evaluate(const std::map> parameters = {}) const; + std::complex evaluate( + const std::map> parameters = {}) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions = {}, - const std::map> parameters = {}) const; + matrix_2 to_matrix( + const std::map dimensions = {}, + const std::map> parameters = {}) const; // comparisons @@ -95,36 +94,40 @@ class scalar_operator { scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; scalar_operator operator-(double other) const; - scalar_operator& operator*=(double other); - scalar_operator& operator/=(double other); - scalar_operator& operator+=(double other); - scalar_operator& operator-=(double other); + scalar_operator &operator*=(double other); + scalar_operator &operator/=(double other); + scalar_operator &operator+=(double other); + scalar_operator &operator-=(double other); scalar_operator operator*(std::complex other) const; scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - scalar_operator& operator*=(std::complex other); - scalar_operator& operator/=(std::complex other); - scalar_operator& operator+=(std::complex other); - scalar_operator& operator-=(std::complex other); + scalar_operator &operator*=(std::complex other); + scalar_operator &operator/=(std::complex other); + scalar_operator &operator+=(std::complex other); + scalar_operator &operator-=(std::complex other); scalar_operator operator*(const scalar_operator &other) const; scalar_operator operator/(const scalar_operator &other) const; scalar_operator operator+(const scalar_operator &other) const; scalar_operator operator-(const scalar_operator &other) const; - scalar_operator& operator*=(const scalar_operator &other); - scalar_operator& operator/=(const scalar_operator &other); - scalar_operator& operator+=(const scalar_operator &other); - scalar_operator& operator-=(const scalar_operator &other); + scalar_operator &operator*=(const scalar_operator &other); + scalar_operator &operator/=(const scalar_operator &other); + scalar_operator &operator+=(const scalar_operator &other); + scalar_operator &operator-=(const scalar_operator &other); /// TODO: implement and test pow friend scalar_operator operator*(scalar_operator &&self, double other); friend scalar_operator operator/(scalar_operator &&self, double other); friend scalar_operator operator+(scalar_operator &&self, double other); friend scalar_operator operator-(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, std::complex other); - friend scalar_operator operator/(scalar_operator &&self, std::complex other); - friend scalar_operator operator+(scalar_operator &&self, std::complex other); - friend scalar_operator operator-(scalar_operator &&self, std::complex other); + friend scalar_operator operator+(scalar_operator &&self, + std::complex other); + friend scalar_operator operator/(scalar_operator &&self, + std::complex other); + friend scalar_operator operator+(scalar_operator &&self, + std::complex other); + friend scalar_operator operator-(scalar_operator &&self, + std::complex other); // left-hand arithmetics @@ -132,13 +135,16 @@ class scalar_operator { friend scalar_operator operator/(double other, const scalar_operator &self); friend scalar_operator operator+(double other, const scalar_operator &self); friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, const scalar_operator &self); - friend scalar_operator operator/(std::complex other, const scalar_operator &self); - friend scalar_operator operator+(std::complex other, const scalar_operator &self); - friend scalar_operator operator-(std::complex other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, + const scalar_operator &self); + friend scalar_operator operator/(std::complex other, + const scalar_operator &self); + friend scalar_operator operator+(std::complex other, + const scalar_operator &self); + friend scalar_operator operator-(std::complex other, + const scalar_operator &self); }; - /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -147,28 +153,26 @@ template // handler needs to inherit from operation_handler class operator_sum { private: - std::tuple, std::vector> m_canonicalize_product(product_operator &prod) const; std::tuple, std::vector> m_canonical_terms() const; - matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms = true) const; void aggregate_terms(); - template - void aggregate_terms(const product_operator &head, Args&& ... args); + template + void aggregate_terms(const product_operator &head, Args &&...args); protected: - operator_sum() = default; std::vector> terms; std::vector coefficients; public: - // read-only properties /// @brief The degrees of freedom that the operator acts on in canonical @@ -182,8 +186,12 @@ class operator_sum { // constructors and destructors - template, Args>...>::value, void>> - operator_sum(const Args&... args); + template , Args>...>::value, + void>> + operator_sum(const Args &...args); operator_sum(const std::vector> &terms); @@ -200,10 +208,10 @@ class operator_sum { // assignments // assignment operator - operator_sum& operator=(const operator_sum &other); + operator_sum &operator=(const operator_sum &other); // move assignment operator - operator_sum& operator=(operator_sum &&other); + operator_sum &operator=(operator_sum &&other); // evaluations @@ -217,20 +225,21 @@ class operator_sum { /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(const std::map &dimensions = {}, - const std::map> ¶meters = {}) const; + matrix_2 to_matrix( + const std::map &dimensions = {}, + const std::map> ¶meters = {}) const; // comparisons - /// @brief True, if the other value is an operator_sum with equivalent terms, - /// and False otherwise. The equality takes into account that operator - /// addition is commutative, as is the product of two operators if they - /// act on different degrees of freedom. - /// The equality comparison does *not* take commutation relations into - /// account, and does not try to reorder terms `blockwise`; it may hence - /// evaluate to False, even if two operators in reality are the same. - /// If the equality evaluates to True, on the other hand, the operators - /// are guaranteed to represent the same transformation for all arguments. + /// @brief True, if the other value is an operator_sum with + /// equivalent terms, and False otherwise. The equality takes into account + /// that operator addition is commutative, as is the product of two operators + /// if they act on different degrees of freedom. The equality comparison does + /// *not* take commutation relations into account, and does not try to reorder + /// terms `blockwise`; it may hence evaluate to False, even if two operators + /// in reality are the same. If the equality evaluates to True, on the other + /// hand, the operators are guaranteed to represent the same transformation + /// for all arguments. bool operator==(const operator_sum &other) const; // unary operators @@ -252,88 +261,100 @@ class operator_sum { operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; + operator_sum + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const; + operator_sum + operator-(const product_operator &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; - operator_sum& operator*=(double other); - operator_sum& operator+=(double other); - operator_sum& operator-=(double other); - operator_sum& operator*=(std::complex other); - operator_sum& operator+=(std::complex other); - operator_sum& operator-=(std::complex other); - operator_sum& operator*=(const scalar_operator &other); - operator_sum& operator+=(const scalar_operator &other); - operator_sum& operator-=(const scalar_operator &other); - operator_sum& operator*=(const HandlerTy &other); - operator_sum& operator+=(const HandlerTy &other); - operator_sum& operator-=(const HandlerTy &other); - operator_sum& operator*=(const product_operator &other); - operator_sum& operator+=(const product_operator &other); - operator_sum& operator-=(const product_operator &other); - operator_sum& operator*=(const operator_sum &other); - operator_sum& operator+=(const operator_sum &other); - operator_sum& operator-=(const operator_sum &other); + operator_sum &operator*=(double other); + operator_sum &operator+=(double other); + operator_sum &operator-=(double other); + operator_sum &operator*=(std::complex other); + operator_sum &operator+=(std::complex other); + operator_sum &operator-=(std::complex other); + operator_sum &operator*=(const scalar_operator &other); + operator_sum &operator+=(const scalar_operator &other); + operator_sum &operator-=(const scalar_operator &other); + operator_sum &operator*=(const HandlerTy &other); + operator_sum &operator+=(const HandlerTy &other); + operator_sum &operator-=(const HandlerTy &other); + operator_sum &operator*=(const product_operator &other); + operator_sum &operator+=(const product_operator &other); + operator_sum &operator-=(const product_operator &other); + operator_sum &operator*=(const operator_sum &other); + operator_sum &operator+=(const operator_sum &other); + operator_sum &operator-=(const operator_sum &other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. - template + // Being a bit permissive here, since otherwise the explicit template + // instantiation is a nightmare. + template friend operator_sum operator*(double other, const operator_sum &self); - template + template friend operator_sum operator+(double other, const operator_sum &self); - template + template friend operator_sum operator-(double other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, const operator_sum &self); - template - friend operator_sum operator+(std::complex other, const operator_sum &self); - template - friend operator_sum operator-(std::complex other, const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - template + template + friend operator_sum operator*(std::complex other, + const operator_sum &self); + template + friend operator_sum operator+(std::complex other, + const operator_sum &self); + template + friend operator_sum operator-(std::complex other, + const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, + const operator_sum &self); + template friend operator_sum operator*(const T &other, const operator_sum &self); - template + template friend operator_sum operator+(const T &other, const operator_sum &self); - template - friend operator_sum operator-(const T &other, const operator_sum &self); - - template - friend operator_sum product_operator::operator*(const operator_sum &other) const; - template - friend operator_sum product_operator::operator+(const operator_sum &other) const; - template - friend operator_sum product_operator::operator-(const operator_sum &other) const; + template + friend operator_sum operator-(const T &other, const operator_sum &self); + + template + friend operator_sum + product_operator::operator*(const operator_sum &other) const; + template + friend operator_sum + product_operator::operator+(const operator_sum &other) const; + template + friend operator_sum + product_operator::operator-(const operator_sum &other) const; }; - /// @brief Represents an operator expression consisting of a product of /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types /// that can. template // handler needs to inherit from operation_handler class product_operator : public operator_sum { -friend class operator_sum; // FIXME: explicitly list members instead? + friend class operator_sum; // FIXME: explicitly list members + // instead? private: - void aggregate_terms(); - template - void aggregate_terms(const HandlerTy &head, Args&& ... args); + template + void aggregate_terms(const HandlerTy &head, Args &&...args); - matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms = true) const; public: - // read-only properties /// @brief The degrees of freedom that the operator acts on in canonical @@ -350,12 +371,17 @@ friend class operator_sum; // FIXME: explicitly list members instead? // constructors and destructors - template...>::value, void>> - product_operator(scalar_operator coefficient, const Args&... args); + template < + class... Args, + class = std::enable_if_t< + std::conjunction...>::value, void>> + product_operator(scalar_operator coefficient, const Args &...args); - product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + product_operator(scalar_operator coefficient, + const std::vector &atomic_operators); - product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + product_operator(scalar_operator coefficient, + std::vector &&atomic_operators); // copy constructor product_operator(const product_operator &other); @@ -368,10 +394,11 @@ friend class operator_sum; // FIXME: explicitly list members instead? // assignments // assignment operator - product_operator& operator=(const product_operator &other); + product_operator & + operator=(const product_operator &other); // move assignment operator - product_operator& operator=(product_operator &&other); + product_operator &operator=(product_operator &&other); // evaluations @@ -385,12 +412,14 @@ friend class operator_sum; // FIXME: explicitly list members instead? /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; + matrix_2 + to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; // comparisons - /// @brief True, if the other value is an operator_sum with equivalent terms, + /// @brief True, if the other value is an operator_sum with + /// equivalent terms, /// and False otherwise. The equality takes into account that operator /// addition is commutative, as is the product of two operators if they /// act on different degrees of freedom. @@ -420,49 +449,65 @@ friend class operator_sum; // FIXME: explicitly list members instead? product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; + product_operator + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const; + operator_sum + operator-(const product_operator &other) const; operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - product_operator& operator*=(double other); - product_operator& operator*=(std::complex other); - product_operator& operator*=(const scalar_operator &other); - product_operator& operator*=(const HandlerTy &other); - product_operator& operator*=(const product_operator &other); + product_operator &operator*=(double other); + product_operator &operator*=(std::complex other); + product_operator &operator*=(const scalar_operator &other); + product_operator &operator*=(const HandlerTy &other); + product_operator & + operator*=(const product_operator &other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. - template - friend product_operator operator*(double other, const product_operator &self); - template - friend operator_sum operator+(double other, const product_operator &self); - template - friend operator_sum operator-(double other, const product_operator &self); - template - friend product_operator operator*(std::complex other, const product_operator &self); - template - friend operator_sum operator+(std::complex other, const product_operator &self); - template - friend operator_sum operator-(std::complex other, const product_operator &self); - template - friend product_operator operator*(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - template - friend product_operator operator*(const T &other, const product_operator &self); - template - friend operator_sum operator+(const T &other, const product_operator &self); - template - friend operator_sum operator-(const T &other, const product_operator &self); + // Being a bit permissive here, since otherwise the explicit template + // instantiation is a nightmare. + template + friend product_operator operator*(double other, + const product_operator &self); + template + friend operator_sum operator+(double other, + const product_operator &self); + template + friend operator_sum operator-(double other, + const product_operator &self); + template + friend product_operator operator*(std::complex other, + const product_operator &self); + template + friend operator_sum operator+(std::complex other, + const product_operator &self); + template + friend operator_sum operator-(std::complex other, + const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, + const product_operator &self); + template + friend product_operator operator*(const T &other, + const product_operator &self); + template + friend operator_sum operator+(const T &other, + const product_operator &self); + template + friend operator_sum operator-(const T &other, + const product_operator &self); }; - class matrix_operator { private: @@ -476,22 +521,22 @@ class matrix_operator { /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. matrix_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), degrees(degrees) {} + : id(operator_id), degrees(degrees) {} // constructor matrix_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), degrees(std::move(degrees)) {} + : id(operator_id), degrees(std::move(degrees)) {} // copy constructor matrix_operator(const matrix_operator &other) - : degrees(other.degrees), id(other.id) {} + : degrees(other.degrees), id(other.id) {} // move constructor - matrix_operator(matrix_operator &&other) - : degrees(std::move(other.degrees)), id(other.id) {} + matrix_operator(matrix_operator &&other) + : degrees(std::move(other.degrees)), id(other.id) {} // assignment operator - matrix_operator& operator=(const matrix_operator& other) { + matrix_operator &operator=(const matrix_operator &other) { if (this != &other) { degrees = other.degrees; id = other.id; @@ -500,9 +545,9 @@ class matrix_operator { } // move assignment operator - matrix_operator& operator=(matrix_operator &&other) { + matrix_operator &operator=(matrix_operator &&other) { degrees = std::move(other.degrees); - id = other.id; + id = other.id; return *this; } @@ -521,8 +566,9 @@ class matrix_operator { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; + matrix_2 + to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. @@ -569,9 +615,11 @@ class matrix_operator { /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - static void define(std::string operator_id, std::vector expected_dimensions, - CallbackFunction &&create) { - auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); + static void define(std::string operator_id, + std::vector expected_dimensions, + CallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, + std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { // todo: make a nice error message to say op already exists @@ -581,7 +629,8 @@ class matrix_operator { }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system -class rydberg_hamiltonian : public operator_sum { +template +class rydberg_hamiltonian : public operator_sum { public: using Coordinate = std::pair; @@ -633,7 +682,6 @@ extern template class product_operator; extern template class operator_sum; #endif - template class OperatorArithmetics { public: @@ -653,7 +701,7 @@ class OperatorArithmetics { }; class EvaluatedMatrix { -friend class MatrixArithmetics; + friend class MatrixArithmetics; private: std::vector m_degrees; @@ -685,7 +733,8 @@ class MatrixArithmetics : public OperatorArithmetics { public: std::map &m_dimensions; // fixme: make const - std::map> &m_parameters; // fixme: make const + std::map> + &m_parameters; // fixme: make const MatrixArithmetics(std::map dimensions, std::map> parameters) @@ -702,8 +751,9 @@ class MatrixArithmetics : public OperatorArithmetics { EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); // Computes the matrix of an ElementaryOperator or ScalarOperator using its // `to_matrix` method. - EvaluatedMatrix - evaluate(std::variant> op); + EvaluatedMatrix evaluate(std::variant> + op); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 18486385fa..5659b01d32 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -107,7 +107,7 @@ class matrix_2 { /// Return a square identity matrix for the given size. static matrix_2 identity(const std::size_t rows); - + /// Kronecker a list of matrices. The list can be any container that has /// iterators defined. template diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 5acde43b42..d7710419fb 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -103,11 +103,13 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } -void assert_product_equal(const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { +void assert_product_equal( + const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + auto sumterms_prod = + ((const cudaq::operator_sum &)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); @@ -120,9 +122,7 @@ void assert_product_equal(const cudaq::product_operator /// the return of this function, since the `-1.0` value /// is going out of scope somewhere down the line in its /// conversion behind the scenes to a scalar operator. -cudaq::scalar_operator negate(cudaq::scalar_operator op) { - return -1.0 * op; -} +cudaq::scalar_operator negate(cudaq::scalar_operator op) { return -1.0 * op; } TEST(OperatorExpressions, checkElementaryAgainstDouble) { std::complex value = 0.125 + 0.125j; @@ -135,7 +135,7 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { auto sum = value + elementary; auto reverse = elementary + value; - auto got_matrix = sum.to_matrix({{0,3}}); + auto got_matrix = sum.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils_0::id_matrix(3); @@ -146,14 +146,15 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `matrix_operator` - `complex` and `complex` - `matrix_operator` + // `matrix_operator` - `complex` and `complex` - + // `matrix_operator` { auto elementary = cudaq::matrix_operator::position(0); auto difference = value - elementary; auto reverse = elementary - value; - auto got_matrix = difference.to_matrix({{0,3}}); + auto got_matrix = difference.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils_0::id_matrix(3); @@ -172,7 +173,7 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { auto product = value * elementary; auto reverse = elementary * value; - auto got_matrix = product.to_matrix({{0,3}}); + auto got_matrix = product.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils_0::id_matrix(3); @@ -229,8 +230,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils_0::parity_matrix(level_count); @@ -253,7 +256,8 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto got_matrix = sum.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils_0::number_matrix(level_count); + auto want_reverse_matrix = + scaled_identity - utils_0::number_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -270,10 +274,13 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { ASSERT_TRUE(reverse.n_terms() == 2); auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::position_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils_0::position_matrix(level_count); + auto want_reverse_matrix = + scaled_identity - utils_0::position_matrix(level_count); utils_0::checkEqual(want_matrix, got_matrix); utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -286,8 +293,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + utils_0::assert_product_equal(product, const_scale_factor, + {cudaq::matrix_operator("momentum", {0})}); + utils_0::assert_product_equal(reverse, const_scale_factor, + {cudaq::matrix_operator("momentum", {0})}); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -311,16 +320,20 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { auto product = self * other; auto reverse = other * self; - utils_0::assert_product_equal(product, other.evaluate(), {cudaq::matrix_operator("create", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + utils_0::assert_product_equal(product, other.evaluate(), + {cudaq::matrix_operator("create", {0})}); + utils_0::assert_product_equal(reverse, other.evaluate(), + {cudaq::matrix_operator("create", {0})}); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = scaled_identity * utils_0::create_matrix(level_count); @@ -452,8 +465,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -469,7 +482,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; utils_0::checkEqual(want_matrix, got_matrix); @@ -480,8 +494,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -496,8 +510,9 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; utils_0::checkEqual(want_matrix, got_matrix); @@ -530,8 +545,10 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + {{"squeezing", value}}); + auto got_reverse_matrix = reverse.to_matrix( + {{0, level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; utils_0::checkEqual(want_matrix, got_matrix); @@ -540,8 +557,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// `operator_sum += matrix_operator` { - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum += cudaq::matrix_operator::displace(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -554,15 +571,16 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}, {{"displacement", value}}); + auto got_matrix = operator_sum.to_matrix( + {{0, level_count}, {1, level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; utils_0::checkEqual(want_matrix, got_matrix); } /// `operator_sum -= matrix_operator` { - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum -= cudaq::matrix_operator::annihilate(0); ASSERT_TRUE(operator_sum.n_terms() == 3); @@ -574,7 +592,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), utils_0::id_matrix(level_count)); - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = + operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = term_0_full + term_1_full - self_full; utils_0::checkEqual(want_matrix, got_matrix); } @@ -582,8 +601,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { /// `operator_sum *= matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum *= self; @@ -599,7 +618,8 @@ TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { utils_0::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = + operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = sum_full * self_full; utils_0::checkEqual(want_matrix, got_matrix); } diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp index 117fe51f48..9d7548b720 100644 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -216,10 +216,10 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { } } -//TEST(OperatorExpressions, checkCustomElementaryOps) { - // pass +// TEST(OperatorExpressions, checkCustomElementaryOps) { +// pass - // ex: - // operator acts upon {0,2} - // user gives us dimensions for {0,1,2} +// ex: +// operator acts upon {0,2} +// user gives us dimensions for {0,1,2} //} diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index abbe10a52f..c959e1f7c7 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -238,8 +238,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -257,8 +257,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -277,8 +279,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` { level_count = 2; - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -286,9 +288,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2,level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -306,8 +308,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -315,8 +317,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -334,8 +338,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum *= scalar_operator` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::momentum(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::momentum(2); sum *= cudaq::scalar_operator(value); @@ -345,7 +349,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils_2::id_matrix(level_count + 1), @@ -355,7 +360,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils_2::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils_2::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils_2::checkEqual(want_matrix, got_matrix); @@ -363,14 +369,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { // `operator_sum += scalar_operator` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::position(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::position(2); sum += cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils_2::id_matrix(level_count + 1), @@ -380,7 +387,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils_2::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils_2::id_matrix((level_count + 1) * level_count); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils_2::checkEqual(want_matrix, got_matrix); @@ -395,7 +403,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils_2::id_matrix(level_count + 1), @@ -405,7 +414,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils_2::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils_2::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) - scaled_identity; utils_2::checkEqual(want_matrix, got_matrix); @@ -419,8 +429,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` and `double * operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -430,16 +440,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } - auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -466,8 +480,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -485,8 +500,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - double` and `double - operator_sum` { - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::number(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::number(2); auto difference = original - double_value; auto reverse = double_value - original; @@ -494,8 +509,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::parity_matrix(level_count)); @@ -513,18 +530,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= double` { - auto sum = cudaq::matrix_operator::squeeze(1) + - cudaq::matrix_operator::squeeze(2); + auto sum = + cudaq::matrix_operator::squeeze(1) + cudaq::matrix_operator::squeeze(2); sum *= double_value; ASSERT_TRUE(sum.n_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -542,14 +561,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum += double_value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -566,14 +585,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum -= double_value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -591,8 +610,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` and `std::complex * // operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * value; auto reverse = value * sum; @@ -610,8 +629,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = product.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -630,8 +651,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` and `std::complex + // operator_sum` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = original + value; auto reverse = value + original; @@ -639,8 +660,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -659,8 +681,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` and `std::complex - // operator_sum` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto difference = original - value; auto reverse = value - original; @@ -668,8 +690,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::create_matrix(level_count)); @@ -687,8 +711,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= std::complex` { - auto sum = cudaq::matrix_operator::displace(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::displace(1) + cudaq::matrix_operator::parity(2); sum *= value; @@ -698,7 +722,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"displacement", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"displacement", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), @@ -722,7 +747,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::momentum_matrix(level_count)); @@ -739,14 +765,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= std::complex` { - auto sum = cudaq::matrix_operator::position(1) + - cudaq::matrix_operator::number(2); + auto sum = + cudaq::matrix_operator::position(1) + cudaq::matrix_operator::number(2); sum -= value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), utils_2::position_matrix(level_count)); @@ -766,8 +792,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { - auto sum_0 = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum_0 = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::create(3); @@ -776,7 +802,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(sum.n_terms() == 5); - auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = sum.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -819,8 +848,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum - operator_sum` { - auto sum_0 = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::position(2); + auto sum_0 = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::position(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::momentum(3); @@ -829,7 +858,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 5); - auto got_matrix = difference.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = difference.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -872,8 +904,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum * operator_sum` { - auto sum_0 = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum_0 = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::create(3); @@ -888,8 +920,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum_product_reverse.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - auto got_matrix = sum_product.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); - auto got_matrix_reverse = sum_product_reverse.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = sum_product.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); + auto got_matrix_reverse = + sum_product_reverse.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -934,8 +973,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum *= operator_sum` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::annihilate(1) + cudaq::matrix_operator::create(3); @@ -946,7 +985,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { for (auto term : sum.get_terms()) ASSERT_TRUE(term.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0,level_count}, {1, level_count+1}, {2, level_count+2}, {3, level_count+3}}); + auto got_matrix = sum.to_matrix({{0, level_count}, + {1, level_count + 1}, + {2, level_count + 2}, + {3, level_count + 3}}); std::vector matrices_0_0; std::vector matrices_0_1; @@ -998,14 +1040,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum += product; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1021,8 +1064,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1039,14 +1081,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum -= product; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1062,8 +1105,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1080,8 +1122,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum *= product; @@ -1090,7 +1132,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(term.n_terms() == 3); } - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { utils_2::id_matrix(level_count + 2), utils_2::id_matrix(level_count + 1), @@ -1106,8 +1149,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { utils_2::id_matrix(level_count)}; std::vector matrices_1_1 = { utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index cb4838fd6e..4155178376 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -105,11 +105,13 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, return difference.exponential(); } -void assert_product_equal(const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { +void assert_product_equal( + const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + auto sumterms_prod = + ((const cudaq::operator_sum &)got).get_terms(); ASSERT_TRUE(sumterms_prod.size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); @@ -118,7 +120,7 @@ void assert_product_equal(const cudaq::product_operator void print(cudaq::matrix_2 matrix) { for (std::size_t i = 0; i < matrix.get_rows(); i++) { for (std::size_t j = 0; j < matrix.get_columns(); j++) { - std::cout << matrix[{i,j}] << " "; + std::cout << matrix[{i, j}] << " "; } std::cout << std::endl; } @@ -137,7 +139,10 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; - utils_1::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {0}), cudaq::matrix_operator("create", {0})}); + utils_1::assert_product_equal( + got, 1., + {cudaq::matrix_operator("annihilate", {0}), + cudaq::matrix_operator("create", {0})}); auto got_matrix = got.to_matrix({{0, level_count}}); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -164,7 +169,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got_reverse.degrees() == want_degrees); auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {1, level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -194,8 +200,9 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {2, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {2, level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -225,8 +232,10 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - auto got_matrix = got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{1,level_count},{2,level_count}}); + auto got_matrix = got.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count}}); auto identity = utils_1::id_matrix(level_count); auto matrix0 = utils_1::annihilate_matrix(level_count); @@ -314,15 +323,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), utils_1::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = value_0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = + value_0 * utils_1::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; @@ -346,8 +357,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -379,8 +391,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -412,8 +425,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -445,8 +460,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -478,8 +495,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::momentum_matrix(level_count)); @@ -498,10 +517,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * complex` { - auto product_op = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::number(1); + auto product_op = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == + std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; @@ -515,8 +535,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -535,25 +556,29 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * double` { - auto product_op = cudaq::matrix_operator::parity(0) * - cudaq::matrix_operator::parity(1); + auto product_op = + cudaq::matrix_operator::parity(0) * cudaq::matrix_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == + std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); - ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::parity_matrix(level_count)); @@ -587,8 +612,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::position_matrix(level_count)); @@ -607,8 +633,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= complex` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::momentum(1); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.n_terms() == 2); @@ -617,7 +643,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -639,12 +665,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= 2.0; ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::annihilate_matrix(level_count)); @@ -660,8 +687,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= scalar_operator` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::momentum(1); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::momentum(1); auto scalar_op = cudaq::scalar_operator(value_0); product *= scalar_op; @@ -673,7 +700,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), utils_1::number_matrix(level_count)); @@ -691,7 +718,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; - std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::map dimensions = { + {0, level_count}, {1, level_count}, {2, level_count + 1}}; // `product_operator + product_operator` { @@ -742,8 +770,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::number(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::momentum(2); + auto term_1 = + cudaq::matrix_operator::create(1) * cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -784,8 +812,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::annihilate(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::parity(2); + auto term_1 = + cudaq::matrix_operator::create(1) * cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -881,8 +909,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -911,8 +940,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -941,8 +972,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -969,7 +1001,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { ASSERT_TRUE(product.n_terms() == 3); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = cudaq::kronecker(utils_1::id_matrix(level_count), @@ -988,14 +1020,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { int level_count = 3; - std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::map dimensions = { + {0, level_count}, {1, level_count}, {2, level_count + 1}}; // `product_operator + operator_sum` { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto original_sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original_sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = product + original_sum; auto reverse = original_sum + product; @@ -1038,8 +1071,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto original_difference = cudaq::matrix_operator::create(1) - - cudaq::matrix_operator::create(2); + auto original_difference = + cudaq::matrix_operator::create(1) - cudaq::matrix_operator::create(2); auto difference = product - original_difference; auto reverse = original_difference - product; @@ -1047,7 +1080,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = difference.to_matrix(dimensions); + auto got_matrix = difference.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. @@ -1082,8 +1115,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { { auto original_product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = original_product * sum; auto reverse = sum * original_product; @@ -1091,7 +1124,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = product.to_matrix(dimensions); + auto got_matrix = product.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); // Cast every term to full Hilbert space. From 299cb28a375624845cbf819332426f95fb9efc41 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 09:28:42 -0800 Subject: [PATCH 199/311] Fixing Rydberg with template HandlerTy Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/rydberg_hamiltonian.cpp | 24 ++++++++++---- unittests/dynamics/rydberg_hamiltonian.cpp | 33 ++++++++++--------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp index 3d8b125ad3..668c9a23db 100644 --- a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp +++ b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp @@ -11,7 +11,8 @@ #include namespace cudaq { -rydberg_hamiltonian::rydberg_hamiltonian( +template +rydberg_hamiltonian::rydberg_hamiltonian( const std::vector &atom_sites, const scalar_operator &litude, const scalar_operator &phase, const scalar_operator &delta_global, const std::vector &atom_filling, @@ -34,22 +35,31 @@ rydberg_hamiltonian::rydberg_hamiltonian( } } -const std::vector & -rydberg_hamiltonian::get_atom_sites() const { +template +const std::vector::Coordinate> & +rydberg_hamiltonian::get_atom_sites() const { return atom_sites; } -const std::vector &rydberg_hamiltonian::get_atom_filling() const { +template +const std::vector & +rydberg_hamiltonian::get_atom_filling() const { return atom_filling; } -const scalar_operator &rydberg_hamiltonian::get_amplitude() const { +template +const scalar_operator &rydberg_hamiltonian::get_amplitude() const { return amplitude; } -const scalar_operator &rydberg_hamiltonian::get_phase() const { return phase; } +template +const scalar_operator &rydberg_hamiltonian::get_phase() const { + return phase; +} -const scalar_operator &rydberg_hamiltonian::get_delta_global() const { +template +const scalar_operator & +rydberg_hamiltonian::get_delta_global() const { return delta_global; } } // namespace cudaq \ No newline at end of file diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp index 2e64cb0666..ef2856c1a6 100644 --- a/unittests/dynamics/rydberg_hamiltonian.cpp +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -13,7 +13,7 @@ using namespace cudaq; TEST(RydbergHamiltonianTest, ConstructorValidInputs) { // Valid atom sites - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators @@ -22,7 +22,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { scalar_operator delta_global(-0.5); // Valid atom filling - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); @@ -35,7 +36,7 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { } TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -46,15 +47,15 @@ TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { // Valid atom filling std::vector atom_filling = {1, 0, 1}; - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global, - atom_filling); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global, atom_filling); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); } TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -65,13 +66,13 @@ TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { // Invalid atom filling size std::vector atom_filling = {1, 0}; - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, - atom_filling), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, + delta_global, atom_filling), std::invalid_argument); } TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -83,13 +84,13 @@ TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { auto delta_local = std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, - {}, delta_local), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, + delta_global, {}, delta_local), std::runtime_error); } TEST(RydbergHamiltonianTest, Accessors) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators @@ -97,7 +98,8 @@ TEST(RydbergHamiltonianTest, Accessors) { scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global); EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), @@ -109,7 +111,7 @@ TEST(RydbergHamiltonianTest, Accessors) { } TEST(RydbergHamiltonianTest, DefaultAtomFilling) { - std::vector atom_sites = { + std::vector::Coordinate> atom_sites = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators @@ -117,7 +119,8 @@ TEST(RydbergHamiltonianTest, DefaultAtomFilling) { scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, + delta_global); std::vector expected_filling(atom_sites.size(), 1); EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); From 4edcc73430dcc11f1b683b4b008332b1fe03f8d0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 09:42:06 -0800 Subject: [PATCH 200/311] Fixing with template typename Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 8 +++++--- unittests/dynamics/test_cudm_helpers.cpp | 23 ++++++++++++----------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index a04869d28c..50e83c5fdf 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -173,10 +173,12 @@ convert_dimensions(const std::vector &mode_extents) { return dimensions; } +template cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatHandle_t handle, const std::map> ¶meters, - const operator_sum &op, const std::vector &mode_extents) { + const operator_sum &op, + const std::vector &mode_extents) { if (op.get_terms().empty()) { throw std::invalid_argument("Operator sum cannot be empty."); } @@ -197,8 +199,8 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( mode_extents.data(), &term)); for (const auto &component : product_op.get_terms()) { - if (std::holds_alternative(component)) { - const auto &elem_op = std::get(component); + if (std::holds_alternative(component)) { + const auto &elem_op = std::get(component); auto subspace_extents = get_subspace_extents(mode_extents, elem_op.degrees); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index e747cc97db..dd322613bb 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,13 +12,14 @@ #include // Initialize operator_sum -cudaq::operator_sum initialize_operator_sum() { +template +cudaq::operator_sum initialize_operator_sum() { std::vector degrees = {0, 1}; // Elementary operators - cudaq::elementary_operator pauli_x("pauli_x", {0}); - cudaq::elementary_operator pauli_z("pauli_z", {1}); - cudaq::elementary_operator identity = cudaq::elementary_operator::identity(0); + cudaq::matrix_operator pauli_x("pauli_x", {0}); + cudaq::matrix_operator pauli_z("pauli_z", {1}); + cudaq::matrix_operator identity = cudaq::matrix_operator::identity(0); auto prod_op_1 = cudaq::scalar_operator(std::complex(1.0, 0.0)) * pauli_x * pauli_z; @@ -26,7 +27,7 @@ cudaq::operator_sum initialize_operator_sum() { auto prod_op_2 = cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; - cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); return op_sum; } @@ -88,11 +89,11 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, - mode_extents); + auto result = cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -103,9 +104,9 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, - op_sum, mode_extents), + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + invalid_handle, {}, op_sum, mode_extents), std::runtime_error); } From d3decea8b853e9a0367f41add8e721d7683e1e27 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 10:37:57 -0800 Subject: [PATCH 201/311] Fixing HandlerTy tenplate by setting it to a value Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 2 +- .../cudaq/dynamics/runge_kutta_integrator.cpp | 53 +++++++++++-------- runtime/cudaq/runge_kutta_integrator.h | 8 +-- unittests/dynamics/rydberg_hamiltonian.cpp | 48 ++++++++--------- unittests/dynamics/test_cudm_helpers.cpp | 2 +- .../dynamics/test_runge_kutta_integrator.cpp | 19 +++---- 6 files changed, 71 insertions(+), 61 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 1abe004c0d..bbbbeeb001 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,7 +16,7 @@ #include namespace cudaq { -template +template > class BaseIntegrator { protected: std::map integrator_options; diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index e966ef6ec8..d562f0ca26 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -12,50 +12,57 @@ using namespace cudaq; namespace cudaq { -void runge_kutta_integrator::integrate(double target_time) { - if (!stepper) { +template +void runge_kutta_integrator::integrate(double target_time) { + if (!this->stepper) { throw std::runtime_error("Time stepper is not initialized."); } - if (integrator_options.find("dt") == integrator_options.end()) { + if (this->integrator_options.find("dt") == this->integrator_options.end()) { throw std::invalid_argument( "Time step size (dt) is missing from integrator options."); } - double dt = integrator_options["dt"]; + double dt = this->integrator_options["dt"]; if (dt <= 0) { throw std::invalid_argument("Invalid time step size for integration."); } - while (t < target_time) { - double step_size = std::min(dt, target_time - t); + while (this->t < target_time) { + double step_size = std::min(dt, target_time - this->t); - std::cout << "Runge-Kutta step at time " << t + std::cout << "Runge-Kutta step at time " << this->t << " with step size: " << step_size << std::endl; - if (substeps_ == 1) { + if (this->substeps_ == 1) { // Euler method (1st order) - cudm_state k1 = stepper->compute(state, t, step_size); - state += k1; - } else if (substeps_ == 2) { + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + this->state += k1; + } else if (this->substeps_ == 2) { // Midpoint method (2nd order) - cudm_state k1 = stepper->compute(state, t, step_size / 2.0); - cudm_state k2 = stepper->compute(k1, t + step_size / 2.0, step_size); - state += (k1 + k2) * 0.5; - } else if (substeps_ == 4) { - // Runge-Kutta method (4th order) - cudm_state k1 = stepper->compute(state, t, step_size / 2.0); + cudm_state k1 = + this->stepper->compute(this->state, this->t, step_size / 2.0); cudm_state k2 = - stepper->compute(k1, t + step_size / 2.0, step_size / 2.0); - cudm_state k3 = stepper->compute(k2, t + step_size / 2.0, step_size); - cudm_state k4 = stepper->compute(k3, t + step_size, step_size); - state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); + this->stepper->compute(k1, this->t + step_size / 2.0, step_size); + this->state += (k1 + k2) * 0.5; + } else if (this->substeps_ == 4) { + // Runge-Kutta method (4th order) + cudm_state k1 = + this->stepper->compute(this->state, this->t, step_size / 2.0); + cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, + step_size / 2.0); + cudm_state k3 = + this->stepper->compute(k2, this->t + step_size / 2.0, step_size); + cudm_state k4 = + this->stepper->compute(k3, this->t + step_size, step_size); + this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } // Update time - t += step_size; + this->t += step_size; } - std::cout << "Integration complete. Final time: " << t << std::endl; + std::cout << "Integration complete. Final time: " << this->t << std::endl; } + } // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index fa9585164f..aa45dc42ad 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -15,7 +15,8 @@ #include namespace cudaq { -class runge_kutta_integrator : public BaseIntegrator { +template > +class runge_kutta_integrator : public BaseIntegrator { public: /// @brief Constructor to initialize the Runge-Kutta integrator /// @param initial_state Initial quantum state. @@ -25,7 +26,8 @@ class runge_kutta_integrator : public BaseIntegrator { runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4) - : BaseIntegrator(std::move(initial_state), t0, stepper), + : BaseIntegrator(std::move(initial_state), t0, + stepper), substeps_(substeps) { if (!stepper) { throw std::invalid_argument("Time stepper must be initialized."); @@ -34,7 +36,7 @@ class runge_kutta_integrator : public BaseIntegrator { if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); } - post_init(); + this->post_init(); } /// @brief Perform Runge-Kutta integration until the target time. diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp index ef2856c1a6..33e7fdff71 100644 --- a/unittests/dynamics/rydberg_hamiltonian.cpp +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -13,8 +13,8 @@ using namespace cudaq; TEST(RydbergHamiltonianTest, ConstructorValidInputs) { // Valid atom sites - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -22,8 +22,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { scalar_operator delta_global(-0.5); // Valid atom filling - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global); + rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, + phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); @@ -36,8 +36,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { } TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -47,16 +47,16 @@ TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { // Valid atom filling std::vector atom_filling = {1, 0, 1}; - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global, atom_filling); + rydberg_hamiltonian> hamiltonian( + atom_sites, amplitude, phase, delta_global, atom_filling); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); } TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -66,14 +66,14 @@ TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { // Invalid atom filling size std::vector atom_filling = {1, 0}; - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, - delta_global, atom_filling), + EXPECT_THROW(rydberg_hamiltonian>( + atom_sites, amplitude, phase, delta_global, atom_filling), std::invalid_argument); } TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -84,22 +84,22 @@ TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { auto delta_local = std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); - EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, - delta_global, {}, delta_local), + EXPECT_THROW(rydberg_hamiltonian>( + atom_sites, amplitude, phase, delta_global, {}, delta_local), std::runtime_error); } TEST(RydbergHamiltonianTest, Accessors) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global); + rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, + phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), @@ -111,16 +111,16 @@ TEST(RydbergHamiltonianTest, Accessors) { } TEST(RydbergHamiltonianTest, DefaultAtomFilling) { - std::vector::Coordinate> atom_sites = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector>::Coordinate> + atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, - delta_global); + rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, + phase, delta_global); std::vector expected_filling(atom_sites.size(), 1); EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index dd322613bb..8c365bfb7c 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -16,7 +16,7 @@ template cudaq::operator_sum initialize_operator_sum() { std::vector degrees = {0, 1}; - // Elementary operators + // Matrix operators cudaq::matrix_operator pauli_x("pauli_x", {0}); cudaq::matrix_operator pauli_z("pauli_z", {1}); cudaq::matrix_operator identity = cudaq::matrix_operator::identity(0); diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 0200d01d8e..6de0c71db6 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -19,7 +19,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::shared_ptr time_stepper_; - std::unique_ptr integrator_; + std::unique_ptr> integrator_; std::unique_ptr state_; void SetUp() override { @@ -41,8 +41,9 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW(integrator_ = std::make_unique( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW(integrator_ = + std::make_unique>( + std::move(*state_), t0, time_stepper_, 4)); ASSERT_NE(integrator_, nullptr); } @@ -60,7 +61,7 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = std::make_unique( + auto eulerIntegrator = std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 1); eulerIntegrator->set_option("dt", 0.1); @@ -69,11 +70,11 @@ TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = std::make_unique( + auto midpointIntegrator = std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 2); - integrator_->set_option("dt", 0.1); - EXPECT_NO_THROW(integrator_->integrate(1.0)); + midpointIntegrator->set_option("dt", 0.1); + EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } // Integration with Runge-Kutta 4 (substeps = 4, which is the default value) @@ -109,7 +110,7 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = std::make_unique( + auto integrator_missing_dt = std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 2); @@ -141,7 +142,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique( + EXPECT_THROW(std::make_unique>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 3), From cf377442ce284d7b88d1bcd3035dd89928b5b711 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 13:49:48 -0800 Subject: [PATCH 202/311] Fixing unittests Signed-off-by: Sachin Pisal --- unittests/dynamics/test_cudm_helpers.cpp | 34 ++++++++++++------ .../dynamics/test_runge_kutta_integrator.cpp | 35 +++++++++++-------- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 8c365bfb7c..ebfcc2d1af 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,22 +12,36 @@ #include // Initialize operator_sum -template +template > cudaq::operator_sum initialize_operator_sum() { std::vector degrees = {0, 1}; // Matrix operators cudaq::matrix_operator pauli_x("pauli_x", {0}); cudaq::matrix_operator pauli_z("pauli_z", {1}); - cudaq::matrix_operator identity = cudaq::matrix_operator::identity(0); + cudaq::product_operator identity = + cudaq::matrix_operator::identity(0); - auto prod_op_1 = cudaq::scalar_operator(std::complex(1.0, 0.0)) * - pauli_x * pauli_z; + std::vector identity_vec = identity.get_terms(); + auto identity_op = cudaq::product_operator( + cudaq::scalar_operator(1.0), identity_vec); + + std::vector pauli_x_vec = {pauli_x}; + auto prod_op_1 = cudaq::product_operator( + cudaq::scalar_operator(1.0), pauli_x_vec); + + std::vector pauli_z_vec = {pauli_z}; + prod_op_1 = prod_op_1 * cudaq::product_operator( + cudaq::scalar_operator(1.0), pauli_z_vec); auto prod_op_2 = - cudaq::scalar_operator(std::complex(0.5, -0.5)) * identity; + cudaq::product_operator( + cudaq::scalar_operator(std::complex(0.5, -0.5))) * + identity_op; - cudaq::operator_sum op_sum({prod_op_1, prod_op_2}); + std::vector> terms = { + prod_op_1, prod_op_2}; + cudaq::operator_sum op_sum(terms); return op_sum; } @@ -89,10 +103,10 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum>(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator( + auto result = cudaq::convert_to_cudensitymat_operator>( handle, {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); @@ -104,9 +118,9 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum>(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator>( invalid_handle, {}, op_sum, mode_extents), std::runtime_error); } diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 6de0c71db6..3180bd3c2b 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -19,7 +19,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::shared_ptr time_stepper_; - std::unique_ptr> integrator_; + std::unique_ptr>> integrator_; std::unique_ptr state_; void SetUp() override { @@ -41,9 +41,10 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW(integrator_ = - std::make_unique>( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW( + integrator_ = + std::make_unique>>( + std::move(*state_), t0, time_stepper_, 4)); ASSERT_NE(integrator_, nullptr); } @@ -61,18 +62,22 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = std::make_unique>( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 1); + auto eulerIntegrator = + std::make_unique>>( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 1); eulerIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); } // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = std::make_unique>( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto midpointIntegrator = + std::make_unique>>( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); midpointIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } @@ -110,9 +115,11 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = std::make_unique>( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto integrator_missing_dt = + std::make_unique>>( + cudm_state(handle_, mock_initial_state_data(), + mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); } @@ -142,7 +149,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique>( + EXPECT_THROW(std::make_unique>>( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 3), From 7d3a8ff89971bb3f91a2f87264d84ba6d9fff8a8 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 4 Feb 2025 15:54:01 -0800 Subject: [PATCH 203/311] Adding missing functions in helpers and updating initialize_operator_sum in test_cudm_helpers Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 204 +++++++++++------------ runtime/cudaq/dynamics/helpers.h | 14 ++ unittests/dynamics/test_cudm_helpers.cpp | 33 +--- 3 files changed, 115 insertions(+), 136 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 5a3d2f8390..b2cff5eab2 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -17,135 +17,129 @@ namespace cudaq { namespace detail { -class _OperatorHelpers { -public: - _OperatorHelpers() = default; - - // Aggregate parameters from multiple mappings. - std::map - aggregate_parameters(const std::vector> - ¶meter_mappings) { - std::map parameter_descriptions; - - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; - } +// Aggregate parameters from multiple mappings. +std::map aggregate_parameters( + const std::vector> ¶meter_mappings) { + std::map parameter_descriptions; + + for (const auto &descriptions : parameter_mappings) { + for (const auto &[key, new_desc] : descriptions) { + if (!parameter_descriptions[key].empty() && !new_desc.empty()) { + parameter_descriptions[key] += "\n---\n" + new_desc; + } else { + parameter_descriptions[key] = new_desc; } } - - return parameter_descriptions; } - // Extract documentation for a specific parameter from docstring. - std::string parameter_docs(const std::string ¶m_name, - const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; - } - - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); + return parameter_descriptions; +} - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; +// Extract documentation for a specific parameter from docstring. +std::string parameter_docs(const std::string ¶m_name, + const std::string &docs) { + if (param_name.empty() || docs.empty()) { + return ""; + } - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if (std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); - } + try { + std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", + std::regex::multiline); + std::regex param_pattern(R"(^\s*)" + param_name + + R"(\s*(\(.*\))?:\s*(.*)$)", + std::regex::multiline); + + std::smatch match; + std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); + std::sregex_iterator end; + + if (it != end) { + std::string params_section = docs.substr(it->position() + it->length()); + if (std::regex_search(params_section, match, param_pattern)) { + std::string param_docs = match.str(2); + return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); } - } catch (...) { - return ""; } - + } catch (...) { return ""; } - // Extract positional arguments and keyword-only arguments. - std::pair, std::map> - args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); - } + return ""; +} + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> +args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args) { + std::vector extracted_args; + std::map kwonly_dict; + + for (const auto &arg : required_args) { + if (kwargs.count(arg)) { + extracted_args.push_back(kwargs.at(arg)); + } else { + throw std::invalid_argument("Missing required argument: " + arg); } + } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); - } + for (const auto &arg : kwonly_args) { + if (kwargs.count(arg)) { + kwonly_dict[arg] = kwargs.at(arg); } - - return {extracted_args, kwonly_dict}; } - /// Generates all possible states for the given dimensions ordered according - /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector generate_all_states(std::vector degrees, - std::map dimensions) { - if (degrees.size() == 0) - return {}; - - std::vector states; - int range = dimensions[degrees[0]]; - for (auto state = 0; state < range; state++) { - states.push_back(std::to_string(state)); - } + return {extracted_args, kwonly_dict}; +} - for (auto idx = 1; idx < degrees.size(); ++idx) { - std::vector result; - for (auto current : states) { - for (auto state = 0; state < dimensions[degrees[idx]]; state++) { - result.push_back(current + std::to_string(state)); - } - } - states = result; - } +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector generate_all_states(std::vector degrees, + std::map dimensions) { + if (degrees.size() == 0) + return {}; - return states; + std::vector states; + int range = dimensions[degrees[0]]; + for (auto state = 0; state < range; state++) { + states.push_back(std::to_string(state)); } - cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation) { - auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); - std::vector> sorted_values; - for (std::size_t permuted : permutation) { - for (std::size_t permuted_again : permutation) { - sorted_values.push_back(matrix[{permuted, permuted_again}]); - } - } - int idx = 0; - for (std::size_t row = 0; row < result.get_rows(); row++) { - for (std::size_t col = 0; col < result.get_columns(); col++) { - result[{row, col}] = sorted_values[idx]; - idx++; + for (auto idx = 1; idx < degrees.size(); ++idx) { + std::vector result; + for (auto current : states) { + for (auto state = 0; state < dimensions[degrees[idx]]; state++) { + result.push_back(current + std::to_string(state)); } } - return result; + states = result; } - std::vector canonicalize_degrees(std::vector degrees) { - std::sort(degrees.begin(), degrees.end(), std::greater()); - return degrees; + return states; +} + +cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation) { + auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); + std::vector> sorted_values; + for (std::size_t permuted : permutation) { + for (std::size_t permuted_again : permutation) { + sorted_values.push_back(matrix[{permuted, permuted_again}]); + } } -}; + int idx = 0; + for (std::size_t row = 0; row < result.get_rows(); row++) { + for (std::size_t col = 0; col < result.get_columns(); col++) { + result[{row, col}] = sorted_values[idx]; + idx++; + } + } + return result; +} + +std::vector canonicalize_degrees(std::vector degrees) { + std::sort(degrees.begin(), degrees.end(), std::greater()); + return degrees; +} } // namespace detail } // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h index ec5446143e..51c8e4bff4 100644 --- a/runtime/cudaq/dynamics/helpers.h +++ b/runtime/cudaq/dynamics/helpers.h @@ -13,6 +13,20 @@ namespace cudaq { namespace detail { +// Aggregate parameters from multiple mappings. +std::map aggregate_parameters( + const std::vector> ¶meter_mappings); + +// Extract documentation for a specific parameter from docstring. +std::string parameter_docs(const std::string ¶m_name, + const std::string &docs); + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> +args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args); + /// Generates all possible states for the given dimensions ordered according /// to the sequence of degrees (ordering is relevant if dimensions differ). std::vector generate_all_states(std::vector degrees, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index ebfcc2d1af..482108e128 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,38 +12,9 @@ #include // Initialize operator_sum -template > +template cudaq::operator_sum initialize_operator_sum() { - std::vector degrees = {0, 1}; - - // Matrix operators - cudaq::matrix_operator pauli_x("pauli_x", {0}); - cudaq::matrix_operator pauli_z("pauli_z", {1}); - cudaq::product_operator identity = - cudaq::matrix_operator::identity(0); - - std::vector identity_vec = identity.get_terms(); - auto identity_op = cudaq::product_operator( - cudaq::scalar_operator(1.0), identity_vec); - - std::vector pauli_x_vec = {pauli_x}; - auto prod_op_1 = cudaq::product_operator( - cudaq::scalar_operator(1.0), pauli_x_vec); - - std::vector pauli_z_vec = {pauli_z}; - prod_op_1 = prod_op_1 * cudaq::product_operator( - cudaq::scalar_operator(1.0), pauli_z_vec); - - auto prod_op_2 = - cudaq::product_operator( - cudaq::scalar_operator(std::complex(0.5, -0.5))) * - identity_op; - - std::vector> terms = { - prod_op_1, prod_op_2}; - cudaq::operator_sum op_sum(terms); - - return op_sum; + return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); } class CuDensityMatTestFixture : public ::testing::Test { From a95d141056b871ed3f2e5ab8d552d14cc796a0f6 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 08:24:56 -0800 Subject: [PATCH 204/311] fixing helpers.h path Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.h | 46 -------------------- runtime/cudaq/helpers.h | 73 +++++++++++++++----------------- 2 files changed, 34 insertions(+), 85 deletions(-) delete mode 100644 runtime/cudaq/dynamics/helpers.h diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h deleted file mode 100644 index 51c8e4bff4..0000000000 --- a/runtime/cudaq/dynamics/helpers.h +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/utils/tensor.h" -#include -#include - -namespace cudaq { -namespace detail { - -// Aggregate parameters from multiple mappings. -std::map aggregate_parameters( - const std::vector> ¶meter_mappings); - -// Extract documentation for a specific parameter from docstring. -std::string parameter_docs(const std::string ¶m_name, - const std::string &docs); - -// Extract positional arguments and keyword-only arguments. -std::pair, std::map> -args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args); - -/// Generates all possible states for the given dimensions ordered according -/// to the sequence of degrees (ordering is relevant if dimensions differ). -std::vector generate_all_states(std::vector degrees, - std::map dimensions); - -// Permutes the given matrix according to the given permutation. -// If states is the current order of vector entries on which the given matrix -// acts, and permuted_states is the desired order of an array on which the -// permuted matrix should act, then the permutation is defined such that -// [states[i] for i in permutation] produces permuted_states. -cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation); - -// Returns the degrees sorted in canonical order. -std::vector canonicalize_degrees(std::vector degrees); -} // namespace detail -} // namespace cudaq diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h index 48e7454adf..51c8e4bff4 100644 --- a/runtime/cudaq/helpers.h +++ b/runtime/cudaq/helpers.h @@ -1,4 +1,4 @@ -/****************************************************************-*- C++ -*-**** +/******************************************************************************* * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * @@ -6,46 +6,41 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#pragma once - -#include -#include -#include +#include "cudaq/utils/tensor.h" #include -#include -#include -#include -#include #include namespace cudaq { -class OperatorHelpers { -public: - // Aggregate parameters from multiple mappings. - static std::map - aggregate_parameters(const std::vector> - ¶meter_mappings); - - // Extract documentation for a specific parameter from docstring. - static std::string parameter_docs(const std::string ¶m_name, - const std::string &docs); - - // Extract positional arguments and keyword-only arguments. - static std::pair, std::map> - args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args); - - // Generate all possible quantum states for given degrees and dimensions. - static std::vector - generate_all_states(const std::vector °rees, - const std::map &dimensions); - - // Permute a given Eigen matrix. - static void permute_matrix(Eigen::MatrixXcd &matrix, - const std::vector &permutation); - - // Canonicalize degrees by sorting in descending order. - static std::vector canonicalize_degrees(const std::vector °rees); -}; +namespace detail { + +// Aggregate parameters from multiple mappings. +std::map aggregate_parameters( + const std::vector> ¶meter_mappings); + +// Extract documentation for a specific parameter from docstring. +std::string parameter_docs(const std::string ¶m_name, + const std::string &docs); + +// Extract positional arguments and keyword-only arguments. +std::pair, std::map> +args_from_kwargs(const std::map &kwargs, + const std::vector &required_args, + const std::vector &kwonly_args); + +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector generate_all_states(std::vector degrees, + std::map dimensions); + +// Permutes the given matrix according to the given permutation. +// If states is the current order of vector entries on which the given matrix +// acts, and permuted_states is the desired order of an array on which the +// permuted matrix should act, then the permutation is defined such that +// [states[i] for i in permutation] produces permuted_states. +cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, + std::vector permutation); + +// Returns the degrees sorted in canonical order. +std::vector canonicalize_degrees(std::vector degrees); +} // namespace detail } // namespace cudaq From a4f94fb0803806785d9da0dde5885cc660e32a48 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 10:26:15 -0800 Subject: [PATCH 205/311] Adding equality comparision operation for matrix_2 Signed-off-by: Sachin Pisal --- runtime/cudaq/utils/tensor.cpp | 17 +++++++++++++++++ runtime/cudaq/utils/tensor.h | 2 ++ 2 files changed, 19 insertions(+) diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index ce94678e57..5298067e9d 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -64,6 +64,23 @@ cudaq::matrix_2 &cudaq::matrix_2::operator-=(const cudaq::matrix_2 &right) { return *this; } +bool cudaq::operator==(const cudaq::matrix_2 &lhs, const cudaq::matrix_2 &rhs) { + if (lhs.get_rows() != rhs.get_rows() || + lhs.get_columns() != rhs.get_columns()) { + return false; + } + + for (std::size_t i = 0; i < lhs.get_rows(); i++) { + for (std::size_t j = 0; j < lhs.get_columns(); j++) { + if (lhs[{i, j}] != rhs[{i, j}]) { + return false; + } + } + } + + return true; +} + cudaq::matrix_2 & cudaq::matrix_2::kronecker_inplace(const cudaq::matrix_2 &right) { Dimensions new_dim{get_rows() * right.get_rows(), diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 5659b01d32..25518fa4a7 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -27,6 +27,8 @@ matrix_2 kronecker(const matrix_2 &, const matrix_2 &); template ::value_type> matrix_2 kronecker(Iterable begin, Iterable end); +// Equality comparison operator. +bool operator==(const matrix_2 &, const matrix_2 &); //===----------------------------------------------------------------------===// From 27dd340cfd3fa72770bb038d7be284c27e0a20b6 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 10:28:11 -0800 Subject: [PATCH 206/311] Fixing the helpers.h path Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/helpers.cpp | 1 + runtime/cudaq/dynamics/manipulation.cpp | 2 +- runtime/cudaq/dynamics/operator_sum.cpp | 2 +- runtime/cudaq/dynamics/product_operators.cpp | 2 +- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index b2cff5eab2..47301ad35f 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace cudaq { diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index e3d6f90447..e81c64ed84 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/helpers.h" #include "cudaq/operators.h" -#include "helpers.h" namespace cudaq { diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 4f37bd536e..90727ae9cf 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/helpers.h" #include "cudaq/operators.h" -#include "helpers.h" #include #include diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 80ea64f6df..0c50f14e95 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/helpers.h" #include "cudaq/operators.h" -#include "helpers.h" #include #include From 4eca1b0e14a5b4bb4053123e5102a8c294c8a4a1 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 14:32:35 -0800 Subject: [PATCH 207/311] Adding HandlerTy Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 2 +- runtime/cudaq/runge_kutta_integrator.h | 2 +- unittests/dynamics/test_cudm_helpers.cpp | 15 +++-- unittests/dynamics/test_helpers.cpp | 74 +++++++++++++----------- 4 files changed, 50 insertions(+), 43 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index bbbbeeb001..1abe004c0d 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,7 +16,7 @@ #include namespace cudaq { -template > +template class BaseIntegrator { protected: std::map integrator_options; diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index aa45dc42ad..253337f267 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -15,7 +15,7 @@ #include namespace cudaq { -template > +template class runge_kutta_integrator : public BaseIntegrator { public: /// @brief Constructor to initialize the Runge-Kutta integrator diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 482108e128..7dce3168db 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -12,8 +12,7 @@ #include // Initialize operator_sum -template -cudaq::operator_sum initialize_operator_sum() { +cudaq::operator_sum initialize_operator_sum() { return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); } @@ -74,11 +73,11 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum>(); + auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator>( - handle, {}, op_sum, mode_extents); + auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, + mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -89,9 +88,9 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum>(); + auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator>( - invalid_handle, {}, op_sum, mode_extents), + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, + op_sum, mode_extents), std::runtime_error); } diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp index aa14cb5c10..dab8d441f4 100644 --- a/unittests/dynamics/test_helpers.cpp +++ b/unittests/dynamics/test_helpers.cpp @@ -10,14 +10,14 @@ #include #include -using namespace cudaq; +using namespace cudaq::detail; TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { std::vector> mappings = { {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}}}; - auto result = OperatorHelpers::aggregate_parameters(mappings); + auto result = aggregate_parameters(mappings); EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); EXPECT_EQ(result["beta"], "Parameter B"); @@ -26,7 +26,7 @@ TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { std::vector> mappings; - auto result = OperatorHelpers::aggregate_parameters(mappings); + auto result = aggregate_parameters(mappings); EXPECT_TRUE(result.empty()); } @@ -37,10 +37,10 @@ TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { " alpha (float): The first parameter.\n" " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); + auto result = parameter_docs("alpha", docstring); EXPECT_EQ(result, "The first parameter."); - result = OperatorHelpers::parameter_docs("beta", docstring); + result = parameter_docs("beta", docstring); EXPECT_EQ(result, "The second parameter."); } @@ -50,13 +50,13 @@ TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { " alpha (float): The first parameter.\n" " beta (int): The second parameter."; - auto result = OperatorHelpers::parameter_docs("gamma", docstring); + auto result = parameter_docs("gamma", docstring); EXPECT_EQ(result, ""); } TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { std::string docstring = ""; - auto result = OperatorHelpers::parameter_docs("alpha", docstring); + auto result = parameter_docs("alpha", docstring); EXPECT_EQ(result, ""); } @@ -64,7 +64,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { std::vector degrees = {0, 1}; std::map dimensions = {{0, 2}, {1, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + auto states = generate_all_states(degrees, dimensions); std::vector expected_states = {"00", "01", "10", "11"}; EXPECT_EQ(states, expected_states); @@ -74,7 +74,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { std::vector degrees = {0, 1, 2}; std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + auto states = generate_all_states(degrees, dimensions); std::vector expected_states = {"000", "001", "010", "011", "100", "101", "110", "111"}; @@ -85,7 +85,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { std::vector degrees; std::map dimensions; - auto states = OperatorHelpers::generate_all_states(degrees, dimensions); + auto states = generate_all_states(degrees, dimensions); EXPECT_TRUE(states.empty()); } @@ -93,55 +93,65 @@ TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { std::vector degrees = {0, 1, 2}; std::map dimensions = {{0, 2}, {1, 2}}; - EXPECT_THROW(OperatorHelpers::generate_all_states(degrees, dimensions), - std::out_of_range); + EXPECT_THROW(generate_all_states(degrees, dimensions), std::out_of_range); } TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { - Eigen::MatrixXcd matrix(2, 2); - matrix << 1, 2, 3, 4; + cudaq::matrix_2 matrix(2, 2); + matrix[{0, 0}] = 1; + matrix[{0, 1}] = 2; + matrix[{1, 0}] = 3; + matrix[{1, 1}] = 4; // Swap rows and columns std::vector permutation = {1, 0}; - OperatorHelpers::permute_matrix(matrix, permutation); + auto permuted_matrix = permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(2, 2); - expected << 4, 3, 2, 1; + cudaq::matrix_2 expected(2, 2); + matrix[{0, 0}] = 4; + matrix[{0, 1}] = 3; + matrix[{1, 0}] = 2; + matrix[{1, 1}] = 1; - EXPECT_EQ(matrix, expected); + EXPECT_EQ(permuted_matrix, expected); } TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { - Eigen::MatrixXcd matrix(3, 3); - matrix << 1, 2, 3, 4, 5, 6, 7, 8, 9; + cudaq::matrix_2 matrix(3, 3); + matrix[{0, 0}] = 1; + matrix[{0, 1}] = 2; + matrix[{0, 2}] = 3; + matrix[{1, 0}] = 4; + matrix[{1, 1}] = 5; + matrix[{1, 2}] = 6; + matrix[{2, 0}] = 7; + matrix[{2, 1}] = 8; + matrix[{2, 2}] = 9; // No change std::vector permutation = {0, 1, 2}; - OperatorHelpers::permute_matrix(matrix, permutation); + auto permuted_matrix = permute_matrix(matrix, permutation); - Eigen::MatrixXcd expected(3, 3); - expected << 1, 2, 3, 4, 5, 6, 7, 8, 9; - - EXPECT_EQ(matrix, expected); + EXPECT_EQ(permuted_matrix, matrix); } TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { std::vector degrees = {3, 1, 2}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + auto sorted = canonicalize_degrees(degrees); EXPECT_EQ(sorted, (std::vector{3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { std::vector degrees = {5, 4, 3, 2, 1}; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + auto sorted = canonicalize_degrees(degrees); EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { std::vector degrees; - auto sorted = OperatorHelpers::canonicalize_degrees(degrees); + auto sorted = canonicalize_degrees(degrees); EXPECT_TRUE(sorted.empty()); } @@ -152,8 +162,7 @@ TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { std::vector required_args = {"alpha", "beta"}; std::vector kwonly_args = {"gamma"}; - auto [args, kwonly] = - OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args); + auto [args, kwonly] = args_from_kwargs(kwargs, required_args, kwonly_args); EXPECT_EQ(args.size(), 2); EXPECT_EQ(args[0], "0.5"); @@ -170,7 +179,6 @@ TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { std::vector required_args = {"alpha", "beta"}; std::vector kwonly_args = {"gamma"}; - EXPECT_THROW( - OperatorHelpers::args_from_kwargs(kwargs, required_args, kwonly_args), - std::invalid_argument); + EXPECT_THROW(args_from_kwargs(kwargs, required_args, kwonly_args), + std::invalid_argument); } From e0709e2678b22361d9b1c91493cd029c2406e149 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 15:56:30 -0800 Subject: [PATCH 208/311] Disabling unneeded (for now) variables Signed-off-by: Sachin Pisal --- runtime/cudaq/base_integrator.h | 30 +++++++++---------- .../cudaq/dynamics/runge_kutta_integrator.cpp | 3 +- runtime/cudaq/runge_kutta_integrator.h | 6 ++-- 3 files changed, 18 insertions(+), 21 deletions(-) diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 1abe004c0d..fed0238ea5 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -16,17 +16,17 @@ #include namespace cudaq { -template +template class BaseIntegrator { protected: std::map integrator_options; TState state; double t; - std::map dimensions; - std::shared_ptr schedule; - std::shared_ptr> hamiltonian; + // std::map dimensions; + // std::shared_ptr schedule; + // std::shared_ptr> hamiltonian; std::shared_ptr> stepper; - std::vector>> collapse_operators; + // std::vector>> collapse_operators; virtual void post_init() = 0; @@ -61,16 +61,16 @@ class BaseIntegrator { } /// @brief Set the system parameters (dimensions, schedule, and operators) - void set_system(const std::map &dimensions, - std::shared_ptr schedule, - std::shared_ptr> hamiltonian, - std::vector>> - collapse_operators = {}) { - this->dimensions = dimensions; - this->schedule = schedule; - this->hamiltonian = hamiltonian; - this->collapse_operators = collapse_operators; - } + // void set_system(const std::map &dimensions, + // std::shared_ptr schedule, + // std::shared_ptr> hamiltonian, + // std::vector>> + // collapse_operators = {}) { + // this->dimensions = dimensions; + // this->schedule = schedule; + // this->hamiltonian = hamiltonian; + // this->collapse_operators = collapse_operators; + // } /// @brief Perform integration to the target time. virtual void integrate(double target_time) = 0; diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index d562f0ca26..87b7f48819 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -12,8 +12,7 @@ using namespace cudaq; namespace cudaq { -template -void runge_kutta_integrator::integrate(double target_time) { +void runge_kutta_integrator::integrate(double target_time) { if (!this->stepper) { throw std::runtime_error("Time stepper is not initialized."); } diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 253337f267..bd6b4445e3 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -15,8 +15,7 @@ #include namespace cudaq { -template -class runge_kutta_integrator : public BaseIntegrator { +class runge_kutta_integrator : public BaseIntegrator { public: /// @brief Constructor to initialize the Runge-Kutta integrator /// @param initial_state Initial quantum state. @@ -26,8 +25,7 @@ class runge_kutta_integrator : public BaseIntegrator { runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4) - : BaseIntegrator(std::move(initial_state), t0, - stepper), + : BaseIntegrator(std::move(initial_state), t0, stepper), substeps_(substeps) { if (!stepper) { throw std::invalid_argument("Time stepper must be initialized."); From 2150ed2e09e41a37380f72fd42e81948117b8b53 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 16:11:15 -0800 Subject: [PATCH 209/311] Adding cudaq::matrix_operator as a handler Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 5 ++- unittests/dynamics/test_cudm_helpers.cpp | 9 ++--- .../dynamics/test_runge_kutta_integrator.cpp | 34 +++++++------------ 3 files changed, 20 insertions(+), 28 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 50e83c5fdf..443020ff62 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -235,9 +235,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( return operator_handle; } catch (const std::exception &e) { - std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() - << std::endl; - throw; + throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); } } @@ -288,4 +286,5 @@ void destroy_array_gpu(void *gpu_array) { HANDLE_CUDA_ERROR(cudaFree(gpu_array)); } } + } // namespace cudaq diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 7dce3168db..15673729b7 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -76,8 +76,9 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ - auto result = cudaq::convert_to_cudensitymat_operator(handle, {}, op_sum, - mode_extents); + auto result = + cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -90,7 +91,7 @@ TEST_F(CuDensityMatTestFixture, InvalidHandle) { std::vector mode_extents = {2, 2}; auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator(invalid_handle, {}, - op_sum, mode_extents), + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + invalid_handle, {}, op_sum, mode_extents), std::runtime_error); } diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 3180bd3c2b..9541316100 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -19,7 +19,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::shared_ptr time_stepper_; - std::unique_ptr>> integrator_; + std::unique_ptr integrator_; std::unique_ptr state_; void SetUp() override { @@ -41,10 +41,8 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW( - integrator_ = - std::make_unique>>( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW(integrator_ = std::make_unique( + std::move(*state_), t0, time_stepper_, 4)); ASSERT_NE(integrator_, nullptr); } @@ -62,22 +60,18 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = - std::make_unique>>( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 1); + auto eulerIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 1); eulerIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); } // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = - std::make_unique>>( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto midpointIntegrator = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); midpointIntegrator->set_option("dt", 0.1); EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } @@ -115,11 +109,9 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = - std::make_unique>>( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + auto integrator_missing_dt = std::make_unique( + cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + 0.0, time_stepper_, 2); EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); } @@ -149,7 +141,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique>>( + EXPECT_THROW(std::make_unique( cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), 0.0, time_stepper_, 3), From d6ef90b8a83f7e326dae6bde4de6881a9e785695 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 16:25:09 -0800 Subject: [PATCH 210/311] Disabling template method Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 84 ++++++++++++++++++++++-- runtime/cudaq/dynamics/cudm_helpers.cpp | 75 --------------------- unittests/dynamics/test_cudm_helpers.cpp | 38 +++++------ 3 files changed, 97 insertions(+), 100 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 36a1ba9688..541ef77822 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -8,6 +8,7 @@ #pragma once +#include "cudaq/cudm_error_handling.h" #include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include @@ -25,12 +26,83 @@ compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents); -template -cudensitymatOperator_t convert_to_cudensitymat_operator( - cudensitymatHandle_t handle, - const std::map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents); +// std::map +// convert_dimensions(const std::vector &mode_extents) { +// std::map dimensions; +// for (size_t i = 0; i < mode_extents.size(); i++) { +// dimensions[static_cast(i)] = static_cast(mode_extents[i]); +// } +// return dimensions; +// } + +// template +// cudensitymatOperator_t convert_to_cudensitymat_operator( +// cudensitymatHandle_t handle, +// const std::map> ¶meters, +// const operator_sum &op, +// const std::vector &mode_extents) { +// if (op.get_terms().empty()) { +// throw std::invalid_argument("Operator sum cannot be empty."); +// } + +// try { +// cudensitymatOperator_t operator_handle; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( +// handle, static_cast(mode_extents.size()), +// mode_extents.data(), &operator_handle)); + +// std::vector elementary_operators; + +// for (const auto &product_op : op.get_terms()) { +// cudensitymatOperatorTerm_t term; + +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( +// handle, static_cast(mode_extents.size()), +// mode_extents.data(), &term)); + +// for (const auto &component : product_op.get_terms()) { +// if (std::holds_alternative(component)) { +// const auto &elem_op = std::get(component); + +// auto subspace_extents = +// get_subspace_extents(mode_extents, elem_op.degrees); +// auto flat_matrix = flatten_matrix( +// elem_op.to_matrix(convert_dimensions(mode_extents), +// parameters)); +// auto cudm_elem_op = +// create_elementary_operator(handle, subspace_extents, +// flat_matrix); + +// elementary_operators.push_back(cudm_elem_op); +// append_elementary_operator_to_term(handle, term, cudm_elem_op, +// elem_op.degrees); +// } else if (std::holds_alternative(component)) +// { +// auto coeff = +// std::get(component).evaluate(parameters); +// append_scalar_to_term(handle, term, coeff); +// } +// } + +// // Append the product operator term to the top-level operator +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), +// {nullptr, nullptr})); + +// // Destroy the term +// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + +// // Cleanup +// for (auto &elem_op : elementary_operators) { +// HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); +// } +// } + +// return operator_handle; +// } catch (const std::exception &e) { +// throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); +// } +// } cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 443020ff62..8e59a2f130 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -164,81 +164,6 @@ compute_lindblad_operator(cudensitymatHandle_t handle, return lindblad_op; } -std::map -convert_dimensions(const std::vector &mode_extents) { - std::map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } - return dimensions; -} - -template -cudensitymatOperator_t convert_to_cudensitymat_operator( - cudensitymatHandle_t handle, - const std::map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents) { - if (op.get_terms().empty()) { - throw std::invalid_argument("Operator sum cannot be empty."); - } - - try { - cudensitymatOperator_t operator_handle; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &operator_handle)); - - std::vector elementary_operators; - - for (const auto &product_op : op.get_terms()) { - cudensitymatOperatorTerm_t term; - - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term)); - - for (const auto &component : product_op.get_terms()) { - if (std::holds_alternative(component)) { - const auto &elem_op = std::get(component); - - auto subspace_extents = - get_subspace_extents(mode_extents, elem_op.degrees); - auto flat_matrix = flatten_matrix( - elem_op.to_matrix(convert_dimensions(mode_extents), parameters)); - auto cudm_elem_op = - create_elementary_operator(handle, subspace_extents, flat_matrix); - - elementary_operators.push_back(cudm_elem_op); - append_elementary_operator_to_term(handle, term, cudm_elem_op, - elem_op.degrees); - } else if (std::holds_alternative(component)) { - auto coeff = - std::get(component).evaluate(parameters); - append_scalar_to_term(handle, term, coeff); - } - } - - // Append the product operator term to the top-level operator - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), - {nullptr, nullptr})); - - // Destroy the term - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - - // Cleanup - for (auto &elem_op : elementary_operators) { - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - } - } - - return operator_handle; - } catch (const std::exception &e) { - throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); - } -} - cudensitymatOperator_t construct_liouvillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 15673729b7..04ee031fd1 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -70,28 +70,28 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { } // Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { - std::vector mode_extents = {2, 2}; +// TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { +// std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); +// auto op_sum = initialize_operator_sum(); - EXPECT_NO_THROW({ - auto result = - cudaq::convert_to_cudensitymat_operator( - handle, {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); - }); -} +// EXPECT_NO_THROW({ +// auto result = +// cudaq::convert_to_cudensitymat_operator( +// handle, {}, op_sum, mode_extents); +// ASSERT_NE(result, nullptr); +// cudensitymatDestroyOperator(result); +// }); +// } // Test invalid handle -TEST_F(CuDensityMatTestFixture, InvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; +// TEST_F(CuDensityMatTestFixture, InvalidHandle) { +// cudensitymatHandle_t invalid_handle = nullptr; - std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); +// std::vector mode_extents = {2, 2}; +// auto op_sum = initialize_operator_sum(); - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( - invalid_handle, {}, op_sum, mode_extents), - std::runtime_error); -} +// EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( +// invalid_handle, {}, op_sum, mode_extents), +// std::runtime_error); +// } From f244d9d31e4c0b1d79b218b6c3a7affcbed7187d Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 17:01:32 -0800 Subject: [PATCH 211/311] Fixing template definition in the header and instatiating it in teh cpp file Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 89 +++---------------------- runtime/cudaq/dynamics/cudm_helpers.cpp | 80 ++++++++++++++++++++++ 2 files changed, 91 insertions(+), 78 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 541ef77822..ede4ee0243 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -8,7 +8,6 @@ #pragma once -#include "cudaq/cudm_error_handling.h" #include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include @@ -26,83 +25,12 @@ compute_lindblad_operator(cudensitymatHandle_t handle, const std::vector &c_ops, const std::vector &mode_extents); -// std::map -// convert_dimensions(const std::vector &mode_extents) { -// std::map dimensions; -// for (size_t i = 0; i < mode_extents.size(); i++) { -// dimensions[static_cast(i)] = static_cast(mode_extents[i]); -// } -// return dimensions; -// } - -// template -// cudensitymatOperator_t convert_to_cudensitymat_operator( -// cudensitymatHandle_t handle, -// const std::map> ¶meters, -// const operator_sum &op, -// const std::vector &mode_extents) { -// if (op.get_terms().empty()) { -// throw std::invalid_argument("Operator sum cannot be empty."); -// } - -// try { -// cudensitymatOperator_t operator_handle; -// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( -// handle, static_cast(mode_extents.size()), -// mode_extents.data(), &operator_handle)); - -// std::vector elementary_operators; - -// for (const auto &product_op : op.get_terms()) { -// cudensitymatOperatorTerm_t term; - -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( -// handle, static_cast(mode_extents.size()), -// mode_extents.data(), &term)); - -// for (const auto &component : product_op.get_terms()) { -// if (std::holds_alternative(component)) { -// const auto &elem_op = std::get(component); - -// auto subspace_extents = -// get_subspace_extents(mode_extents, elem_op.degrees); -// auto flat_matrix = flatten_matrix( -// elem_op.to_matrix(convert_dimensions(mode_extents), -// parameters)); -// auto cudm_elem_op = -// create_elementary_operator(handle, subspace_extents, -// flat_matrix); - -// elementary_operators.push_back(cudm_elem_op); -// append_elementary_operator_to_term(handle, term, cudm_elem_op, -// elem_op.degrees); -// } else if (std::holds_alternative(component)) -// { -// auto coeff = -// std::get(component).evaluate(parameters); -// append_scalar_to_term(handle, term, coeff); -// } -// } - -// // Append the product operator term to the top-level operator -// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( -// handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), -// {nullptr, nullptr})); - -// // Destroy the term -// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - -// // Cleanup -// for (auto &elem_op : elementary_operators) { -// HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); -// } -// } - -// return operator_handle; -// } catch (const std::exception &e) { -// throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); -// } -// } +template +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map> ¶meters, + const operator_sum &op, + const std::vector &mode_extents); cudensitymatOperator_t construct_liovillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, @@ -114,4 +42,9 @@ void *create_array_gpu(const std::vector> &cpu_array); // Function to detsroy a previously created array copy in GPU memory void destroy_array_gpu(void *gpu_array); + +extern template cudensitymatOperator_t +convert_to_cudensitymat_operator( + cudensitymatHandle_t, const std::map> &, + const operator_sum &, const std::vector &); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 8e59a2f130..a96ddd6465 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -164,6 +164,81 @@ compute_lindblad_operator(cudensitymatHandle_t handle, return lindblad_op; } +std::map +convert_dimensions(const std::vector &mode_extents) { + std::map dimensions; + for (size_t i = 0; i < mode_extents.size(); i++) { + dimensions[static_cast(i)] = static_cast(mode_extents[i]); + } + return dimensions; +} + +template +cudensitymatOperator_t convert_to_cudensitymat_operator( + cudensitymatHandle_t handle, + const std::map> ¶meters, + const operator_sum &op, + const std::vector &mode_extents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + + try { + cudensitymatOperator_t operator_handle; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &operator_handle)); + + std::vector elementary_operators; + + for (const auto &product_op : op.get_terms()) { + cudensitymatOperatorTerm_t term; + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term)); + + for (const auto &component : product_op.get_terms()) { + if (auto elem_op = + dynamic_cast(&component)) { + auto subspace_extents = + get_subspace_extents(mode_extents, elem_op->degrees); + auto flat_matrix = flatten_matrix( + elem_op->to_matrix(convert_dimensions(mode_extents), parameters)); + auto cudm_elem_op = + create_elementary_operator(handle, subspace_extents, flat_matrix); + + elementary_operators.push_back(cudm_elem_op); + append_elementary_operator_to_term(handle, term, cudm_elem_op, + elem_op->degrees); + // } else if (auto scalar_op = static_cast(&component)) { + // auto coeff = + // scalar_op->evaluate(parameters); + // append_scalar_to_term(handle, term, coeff); + } + } + + // Append the product operator term to the top-level operator + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); + + // Destroy the term + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + + // Cleanup + for (auto &elem_op : elementary_operators) { + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + } + } + + return operator_handle; + } catch (const std::exception &e) { + throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); + } +} + cudensitymatOperator_t construct_liouvillian( cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, @@ -212,4 +287,9 @@ void destroy_array_gpu(void *gpu_array) { } } +template cudensitymatOperator_t +convert_to_cudensitymat_operator( + cudensitymatHandle_t, const std::map> &, + const operator_sum &, const std::vector &); + } // namespace cudaq From 6c0973784a3762cf882d6de00a3a3c7a041bd8f7 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 17:03:54 -0800 Subject: [PATCH 212/311] Enabling tests Signed-off-by: Sachin Pisal --- unittests/dynamics/test_cudm_helpers.cpp | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 04ee031fd1..15673729b7 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -70,28 +70,28 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { } // Test for convert_to_cudensitymat_operator -// TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { -// std::vector mode_extents = {2, 2}; +TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { + std::vector mode_extents = {2, 2}; -// auto op_sum = initialize_operator_sum(); + auto op_sum = initialize_operator_sum(); -// EXPECT_NO_THROW({ -// auto result = -// cudaq::convert_to_cudensitymat_operator( -// handle, {}, op_sum, mode_extents); -// ASSERT_NE(result, nullptr); -// cudensitymatDestroyOperator(result); -// }); -// } + EXPECT_NO_THROW({ + auto result = + cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); + cudensitymatDestroyOperator(result); + }); +} // Test invalid handle -// TEST_F(CuDensityMatTestFixture, InvalidHandle) { -// cudensitymatHandle_t invalid_handle = nullptr; +TEST_F(CuDensityMatTestFixture, InvalidHandle) { + cudensitymatHandle_t invalid_handle = nullptr; -// std::vector mode_extents = {2, 2}; -// auto op_sum = initialize_operator_sum(); + std::vector mode_extents = {2, 2}; + auto op_sum = initialize_operator_sum(); -// EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( -// invalid_handle, {}, op_sum, mode_extents), -// std::runtime_error); -// } + EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( + invalid_handle, {}, op_sum, mode_extents), + std::runtime_error); +} From 188151904a12475d88d44f21cfe6d818aad50e90 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 17:43:34 -0800 Subject: [PATCH 213/311] Fixing Rydberg and renaming fixture in test_cudm_helpers Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 1 + .../cudaq/dynamics/rydberg_hamiltonian.cpp | 24 +++------ runtime/cudaq/operators.h | 3 +- unittests/dynamics/rydberg_hamiltonian.cpp | 53 +++++++++---------- unittests/dynamics/test_cudm_helpers.cpp | 12 ++--- 5 files changed, 40 insertions(+), 53 deletions(-) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 170c7b963d..241fab28c9 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -25,6 +25,7 @@ set(CUDAQ_OPS_SRC schedule.cpp manipulation.cpp helpers.cpp + rydberg_hamiltonian.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp index 668c9a23db..3d8b125ad3 100644 --- a/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp +++ b/runtime/cudaq/dynamics/rydberg_hamiltonian.cpp @@ -11,8 +11,7 @@ #include namespace cudaq { -template -rydberg_hamiltonian::rydberg_hamiltonian( +rydberg_hamiltonian::rydberg_hamiltonian( const std::vector &atom_sites, const scalar_operator &litude, const scalar_operator &phase, const scalar_operator &delta_global, const std::vector &atom_filling, @@ -35,31 +34,22 @@ rydberg_hamiltonian::rydberg_hamiltonian( } } -template -const std::vector::Coordinate> & -rydberg_hamiltonian::get_atom_sites() const { +const std::vector & +rydberg_hamiltonian::get_atom_sites() const { return atom_sites; } -template -const std::vector & -rydberg_hamiltonian::get_atom_filling() const { +const std::vector &rydberg_hamiltonian::get_atom_filling() const { return atom_filling; } -template -const scalar_operator &rydberg_hamiltonian::get_amplitude() const { +const scalar_operator &rydberg_hamiltonian::get_amplitude() const { return amplitude; } -template -const scalar_operator &rydberg_hamiltonian::get_phase() const { - return phase; -} +const scalar_operator &rydberg_hamiltonian::get_phase() const { return phase; } -template -const scalar_operator & -rydberg_hamiltonian::get_delta_global() const { +const scalar_operator &rydberg_hamiltonian::get_delta_global() const { return delta_global; } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 2a9281ace9..47582c8a11 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -629,8 +629,7 @@ class matrix_operator { }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system -template -class rydberg_hamiltonian : public operator_sum { +class rydberg_hamiltonian { public: using Coordinate = std::pair; diff --git a/unittests/dynamics/rydberg_hamiltonian.cpp b/unittests/dynamics/rydberg_hamiltonian.cpp index 33e7fdff71..f3883366ba 100644 --- a/unittests/dynamics/rydberg_hamiltonian.cpp +++ b/unittests/dynamics/rydberg_hamiltonian.cpp @@ -1,9 +1,9 @@ /******************************************************************************* * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ #include "cudaq/operators.h" @@ -13,8 +13,8 @@ using namespace cudaq; TEST(RydbergHamiltonianTest, ConstructorValidInputs) { // Valid atom sites - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -22,8 +22,7 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { scalar_operator delta_global(-0.5); // Valid atom filling - rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, - phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling().size(), atom_sites.size()); @@ -36,8 +35,8 @@ TEST(RydbergHamiltonianTest, ConstructorValidInputs) { } TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -47,16 +46,16 @@ TEST(RydbergHamiltonianTest, ConstructorWithAtomFilling) { // Valid atom filling std::vector atom_filling = {1, 0, 1}; - rydberg_hamiltonian> hamiltonian( - atom_sites, amplitude, phase, delta_global, atom_filling); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling); EXPECT_EQ(hamiltonian.get_atom_sites().size(), atom_sites.size()); EXPECT_EQ(hamiltonian.get_atom_filling(), atom_filling); } TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -66,14 +65,14 @@ TEST(RydbergHamiltonianTest, InvalidAtomFillingSize) { // Invalid atom filling size std::vector atom_filling = {1, 0}; - EXPECT_THROW(rydberg_hamiltonian>( - atom_sites, amplitude, phase, delta_global, atom_filling), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + atom_filling), std::invalid_argument); } TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); @@ -84,22 +83,21 @@ TEST(RydbergHamiltonianTest, UnsupportedLocalDetuning) { auto delta_local = std::make_pair(scalar_operator(0.5), std::vector{0.1, 0.2, 0.3}); - EXPECT_THROW(rydberg_hamiltonian>( - atom_sites, amplitude, phase, delta_global, {}, delta_local), + EXPECT_THROW(rydberg_hamiltonian(atom_sites, amplitude, phase, delta_global, + {}, delta_local), std::runtime_error); } TEST(RydbergHamiltonianTest, Accessors) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, - phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); EXPECT_EQ(hamiltonian.get_atom_sites(), atom_sites); EXPECT_EQ(hamiltonian.get_amplitude().evaluate({}), @@ -111,16 +109,15 @@ TEST(RydbergHamiltonianTest, Accessors) { } TEST(RydbergHamiltonianTest, DefaultAtomFilling) { - std::vector>::Coordinate> - atom_sites = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; + std::vector atom_sites = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}}; // Valid operators scalar_operator amplitude(1.0); scalar_operator phase(0.0); scalar_operator delta_global(-0.5); - rydberg_hamiltonian> hamiltonian(atom_sites, amplitude, - phase, delta_global); + rydberg_hamiltonian hamiltonian(atom_sites, amplitude, phase, delta_global); std::vector expected_filling(atom_sites.size(), 1); EXPECT_EQ(hamiltonian.get_atom_filling(), expected_filling); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 15673729b7..24ec623f8f 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -16,7 +16,7 @@ cudaq::operator_sum initialize_operator_sum() { return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); } -class CuDensityMatTestFixture : public ::testing::Test { +class CuDensityMatHelpersTestFixture : public ::testing::Test { protected: cudensitymatHandle_t handle; cudaStream_t stream; @@ -30,7 +30,7 @@ class CuDensityMatTestFixture : public ::testing::Test { }; // Test for initialize_state -TEST_F(CuDensityMatTestFixture, InitializeState) { +TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { std::vector mode_extents = {2}; std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; @@ -41,7 +41,7 @@ TEST_F(CuDensityMatTestFixture, InitializeState) { } // Test for scale_state -TEST_F(CuDensityMatTestFixture, ScaleState) { +TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { std::vector mode_extents = {2}; std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; @@ -54,7 +54,7 @@ TEST_F(CuDensityMatTestFixture, ScaleState) { } // Test for compute_lindblad_op -TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { +TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { std::vector mode_extents = {2, 2}; cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); @@ -70,7 +70,7 @@ TEST_F(CuDensityMatTestFixture, ComputeLindbladOp) { } // Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { +TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { std::vector mode_extents = {2, 2}; auto op_sum = initialize_operator_sum(); @@ -85,7 +85,7 @@ TEST_F(CuDensityMatTestFixture, ConvertToCuDensityMatOperator) { } // Test invalid handle -TEST_F(CuDensityMatTestFixture, InvalidHandle) { +TEST_F(CuDensityMatHelpersTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; std::vector mode_extents = {2, 2}; From d42e173f588afe5eabae44b1753c63b7bd5ee5ae Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 5 Feb 2025 18:28:22 -0800 Subject: [PATCH 214/311] * Enabling dynamic_cast for scalar operator * Fixing degrees in unittest when creating operator_sum Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 25 +++++++++++++++++------- runtime/cudaq/operators.h | 2 +- unittests/dynamics/test_cudm_helpers.cpp | 2 +- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index a96ddd6465..667c53bf52 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -85,6 +85,10 @@ void append_elementary_operator_to_term( throw std::invalid_argument("Degrees vector cannot be empty."); } + if (!elem_op) { + throw std::invalid_argument("elem_op cannot be null."); + } + std::vector elem_ops = {elem_op}; std::vector modeActionDuality(degrees.size(), 0); @@ -147,6 +151,11 @@ compute_lindblad_operator(cudensitymatHandle_t handle, cudensitymatElementaryOperator_t cudm_elem_op = create_elementary_operator(handle, mode_extents, flat_matrix); + if (!cudm_elem_op) { + throw std::runtime_error( + "Failed to create elementary operator in compute_lindblad_operator."); + } + // Append the elementary operator to the term std::vector degrees = {0, 1}; append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); @@ -199,7 +208,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( mode_extents.data(), &term)); for (const auto &component : product_op.get_terms()) { - if (auto elem_op = + if (const auto *elem_op = dynamic_cast(&component)) { auto subspace_extents = get_subspace_extents(mode_extents, elem_op->degrees); @@ -211,11 +220,11 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( elementary_operators.push_back(cudm_elem_op); append_elementary_operator_to_term(handle, term, cudm_elem_op, elem_op->degrees); - // } else if (auto scalar_op = static_cast(&component)) { - // auto coeff = - // scalar_op->evaluate(parameters); - // append_scalar_to_term(handle, term, coeff); + } else if (const auto *scalar_op = + dynamic_cast( + &component)) { + auto coeff = scalar_op->evaluate(parameters); + append_scalar_to_term(handle, term, coeff); } } @@ -235,7 +244,9 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( return operator_handle; } catch (const std::exception &e) { - throw std::runtime_error("Error in convert_to_cudensitymat_operator!"); + std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() + << std::endl; + throw; } } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 47582c8a11..d818c55d68 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -551,7 +551,7 @@ class matrix_operator { return *this; } - ~matrix_operator() = default; + virtual ~matrix_operator() = default; /// @brief The degrees of freedom that the operator acts on in canonical /// order. diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 24ec623f8f..c5d5e9759c 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -13,7 +13,7 @@ // Initialize operator_sum cudaq::operator_sum initialize_operator_sum() { - return cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + return cudaq::matrix_operator::create(0) + cudaq::matrix_operator::create(1); } class CuDensityMatHelpersTestFixture : public ::testing::Test { From 7da22de03837f87fe0678f05359c39c79947079f Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 6 Feb 2025 02:46:38 +0000 Subject: [PATCH 215/311] Minor cmake restructure Signed-off-by: Thien Nguyen --- CMakeLists.txt | 3 +++ unittests/CMakeLists.txt | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b9a0acaaa..1a44cf7494 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -151,6 +151,9 @@ endif() if(NOT CUTENSORNET_ROOT) SET(CUTENSORNET_ROOT "$ENV{CUQUANTUM_INSTALL_PREFIX}") endif() +if(NOT CUDENSITYMAT_ROOT) + SET(CUDENSITYMAT_ROOT "$ENV{CUQUANTUM_INSTALL_PREFIX}") +endif() if(NOT CUTENSOR_ROOT) SET(CUTENSOR_ROOT "$ENV{CUTENSOR_INSTALL_PREFIX}") endif() diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 7b48ef7289..a5db4a6718 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -44,12 +44,6 @@ set(CUDAQ_RUNTIME_TEST_SOURCES common/NoiseModelTester.cpp integration/tracer_tester.cpp integration/gate_library_tester.cpp - dynamics/test_runge_kutta_integrator.cpp - dynamics/test_helpers.cpp - dynamics/rydberg_hamiltonian.cpp - dynamics/test_cudm_helpers.cpp - dynamics/test_cudm_state.cpp - dynamics/test_cudm_time_stepper.cpp ) # Make it so we can get function symbols @@ -81,9 +75,7 @@ macro (create_tests_with_backend NVQIR_BACKEND EXTRA_BACKEND_TESTER) fmt::fmt-header-only cudaq-platform-default cudaq-builder - gtest_main - $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 - /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12) + gtest_main) set(TEST_LABELS "") if (${NVQIR_BACKEND} STREQUAL "qpp") target_compile_definitions(${TEST_EXE_NAME} PRIVATE -DCUDAQ_SIMULATION_SCALAR_FP64) @@ -289,11 +281,36 @@ target_link_libraries(test_operators cudaq-operators cudaq gtest_main - $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0 - /usr/local/cuda-12.0/targets/x86_64-linux/lib/libcudart.so.12 fmt::fmt-header-only) gtest_discover_tests(test_operators) +if (CUDA_FOUND) + find_package(CUDAToolkit REQUIRED) + + # Create an executable for dynamics UnitTests + set(CUDAQ_DYNAMICS_TEST_SOURCES + dynamics/test_runge_kutta_integrator.cpp + dynamics/test_helpers.cpp + dynamics/rydberg_hamiltonian.cpp + dynamics/test_cudm_helpers.cpp + dynamics/test_cudm_state.cpp + dynamics/test_cudm_time_stepper.cpp + ) + add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) + if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) + target_link_options(test_dynamics PRIVATE -Wl,--no-as-needed) + endif() + target_link_libraries(test_dynamics + PRIVATE + cudaq-spin + cudaq-operators + cudaq + ${CUDENSITYMAT_ROOT}/lib/libcudensitymat.so.0 + CUDA::cudart_static + gtest_main + fmt::fmt-header-only) + gtest_discover_tests(test_dynamics) +endif() add_subdirectory(plugin) # build the test qudit execution manager From 0a0b435ac8d72a34f038d8bbed197034154c64d7 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 13:17:57 -0800 Subject: [PATCH 216/311] Adding cudm_op_conversion interface Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 61 ++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 runtime/cudaq/cudm_op_conversion.h diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h new file mode 100644 index 0000000000..73e927515e --- /dev/null +++ b/runtime/cudaq/cudm_op_conversion.h @@ -0,0 +1,61 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/cudm_helpers.h" +#include "cudaq/operators.h" +#include "cudaq/schedule.h" +#include +#include + +namespace cudaq { +class cudm_op_conversion { +public: + cudm_op_conversion(const cudensitymatHandle_t handle, + const std::map &dimensions, + const std::shared_ptr schedule = nullptr); + + // Tensor product of two operator terms + cudensitymatOperatorTerm_t tensor(const cudensitymatOperatorTerm_t &op1, + const cudensitymatOperatorTerm_t &op2); + + // Multiplication of two operator terms + cudensitymatOperatorTerm_t mul(const cudensitymatOperatorTerm_t &op1, + const cudensitymatOperatorTerm_t &op2); + + // Addition of two operator terms + cudensitymatOperatorTerm_t add(const cudensitymatOperatorTerm_t &op1, + const cudensitymatOperatorTerm_t &op2); + + // Evaluate an operator and convert it to cudensitymatOperatorTerm_t + cudensitymatOperatorTerm_t evaluate(const matrix_operator &op); + + // Convert a scalar to a cudensitymat operator term + cudensitymatOperatorTerm_t _scalar_to_op(const scalar_operator &scalar); + + // Multiplies a scalar callback with a cudensitymat operator term + cudensitymatOperatorTerm_t + _callback_mult_op(cudensitymatScalarCallback_t scalar, + cudensitymatOperatorTerm_t op); + + // Wrap a matrix operator as a cudensitymat tensor callback + cudensitymatOperatorTerm_t _wrap_callback_tensor(const matrix_operator &op); + +private: + std::map dimensions_; + std::shared_ptr schedule_; + cudensitymatHandle_t handle_; + + std::map> + _termtoElemOps; + std::map> _termtoModes; + std::map> _termtoDuals; +}; +} // namespace cudaq \ No newline at end of file From 281079cf77dbc899a0f5e8a3fb277d47737a2a4f Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 13:38:46 -0800 Subject: [PATCH 217/311] Fixing the return type of _wrap_callback_tensor Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 73e927515e..8217e75412 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -45,7 +45,7 @@ class cudm_op_conversion { cudensitymatOperatorTerm_t op); // Wrap a matrix operator as a cudensitymat tensor callback - cudensitymatOperatorTerm_t _wrap_callback_tensor(const matrix_operator &op); + cudensitymatTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); private: std::map dimensions_; From 700fc35fc8e9b88a78c45a0cef5cbb0b661ee684 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 14:28:17 -0800 Subject: [PATCH 218/311] Updating cudm_op_conversion interface to accommodate wrap callback and std::variant Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 55 +++++++++++++++++------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 8217e75412..462a8eb477 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -22,40 +22,47 @@ class cudm_op_conversion { const std::shared_ptr schedule = nullptr); // Tensor product of two operator terms - cudensitymatOperatorTerm_t tensor(const cudensitymatOperatorTerm_t &op1, - const cudensitymatOperatorTerm_t &op2); + std::variant + tensor(const std::variant &op1, + const std::variant &op2); // Multiplication of two operator terms - cudensitymatOperatorTerm_t mul(const cudensitymatOperatorTerm_t &op1, - const cudensitymatOperatorTerm_t &op2); + std::variant + mul(const std::variant &op1, + const std::variant &op2); // Addition of two operator terms - cudensitymatOperatorTerm_t add(const cudensitymatOperatorTerm_t &op1, - const cudensitymatOperatorTerm_t &op2); + std::variant + add(const std::variant &op1, + const std::variant &op2); // Evaluate an operator and convert it to cudensitymatOperatorTerm_t - cudensitymatOperatorTerm_t evaluate(const matrix_operator &op); - - // Convert a scalar to a cudensitymat operator term - cudensitymatOperatorTerm_t _scalar_to_op(const scalar_operator &scalar); - - // Multiplies a scalar callback with a cudensitymat operator term - cudensitymatOperatorTerm_t - _callback_mult_op(cudensitymatScalarCallback_t scalar, - cudensitymatOperatorTerm_t op); - - // Wrap a matrix operator as a cudensitymat tensor callback - cudensitymatTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); + std::variant + evaluate(const std::variant> &op); private: + cudensitymatHandle_t handle_; std::map dimensions_; std::shared_ptr schedule_; - cudensitymatHandle_t handle_; - std::map> - _termtoElemOps; - std::map> _termtoModes; - std::map> _termtoDuals; + cudensitymatOperatorTerm_t + _callback_mult_op(const cudensitymatWrappedScalarCallback_t &scalar, + const cudensitymatOperatorTerm_t &op); + cudensitymatOperatorTerm_t + _scalar_to_op(const cudensitymatWrappedScalarCallback_t &scalar); + cudensitymatWrappedTensorCallback_t _wrap_callback(const scalar_operator &op); + cudensitymatWrappedTensorCallback_t + _wrap_callback_tensor(const matrix_operator &op); }; } // namespace cudaq \ No newline at end of file From fc7d038c82e1340c7f4c16027dea37c301d7f665 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 17:43:24 -0800 Subject: [PATCH 219/311] Adding cudm_op_conversion implementation, exposing get_generator for the scalar_operator Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 6 +- runtime/cudaq/dynamics/CMakeLists.txt | 1 + runtime/cudaq/dynamics/cudm_op_conversion.cpp | 286 ++++++++++++++++++ runtime/cudaq/dynamics/scalar_operators.cpp | 4 + runtime/cudaq/operators.h | 2 + 5 files changed, 296 insertions(+), 3 deletions(-) create mode 100644 runtime/cudaq/dynamics/cudm_op_conversion.cpp diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 462a8eb477..50395f0c56 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -47,9 +47,9 @@ class cudm_op_conversion { // Evaluate an operator and convert it to cudensitymatOperatorTerm_t std::variant + std::complex> evaluate(const std::variant> &op); + product_operator> &op); private: cudensitymatHandle_t handle_; @@ -61,7 +61,7 @@ class cudm_op_conversion { const cudensitymatOperatorTerm_t &op); cudensitymatOperatorTerm_t _scalar_to_op(const cudensitymatWrappedScalarCallback_t &scalar); - cudensitymatWrappedTensorCallback_t _wrap_callback(const scalar_operator &op); + cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &op); cudensitymatWrappedTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); }; diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 241fab28c9..95d56934f1 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -26,6 +26,7 @@ set(CUDAQ_OPS_SRC manipulation.cpp helpers.cpp rydberg_hamiltonian.cpp + cudm_op_conversion.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp new file mode 100644 index 0000000000..b16f4231f4 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -0,0 +1,286 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_op_conversion.h" +#include "cudaq/cudm_error_handling.h" +#include +#include +#include + +using namespace cudaq; + +namespace cudaq { +cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, + const std::map &dimensions, + std::shared_ptr schedule) + : handle_(handle), dimensions_(dimensions), schedule_(schedule) {} + +cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( + const cudensitymatWrappedScalarCallback_t &scalar) { + cudensitymatOperatorTerm_t op_term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &op_term)); + + cudensitymatElementaryOperator_t identity; + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle_, 1, nullptr, CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, + CUDA_C_64F, nullptr, {nullptr, nullptr}, &identity)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle_, op_term, 1, &identity, nullptr, nullptr, {1.0, 0.0}, scalar)); + + return op_term; +} + +cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( + const cudensitymatWrappedScalarCallback_t &scalar, + const cudensitymatOperatorTerm_t &op) { + if (!op) { + throw std::invalid_argument("Invalid operator term (nullptr)."); + } + + cudensitymatOperatorTerm_t new_opterm; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &new_opterm)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_opterm, op, 0, + {1.0, 0.0}, scalar)); + + return new_opterm; +} + +std::variant +cudm_op_conversion::tensor( + const std::variant &op1, + const std::variant &op2) { + if (std::holds_alternative(op1) || + std::holds_alternative(op2)) { + return std::get(op1) * std::get(op2); + } + + cudensitymatOperatorTerm_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &result)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op1), 0, {1.0, 0.0}, + {nullptr, nullptr})); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op2), 0, {1.0, 0.0}, + {nullptr, nullptr})); + + return result; +} + +std::variant +cudm_op_conversion::mul( + const std::variant &op1, + const std::variant &op2) { + return tensor(op1, op2); +} + +std::variant +cudm_op_conversion::add( + const std::variant &op1, + const std::variant &op2) { + if (std::holds_alternative(op1) || + std::holds_alternative(op2)) { + return std::get(op1) + std::get(op2); + } + + cudensitymatOperatorTerm_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + nullptr, &result)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op1), 0, {1.0, 0.0}, + {nullptr, nullptr})); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, result, std::get(op2), 0, {1.0, 0.0}, + {nullptr, nullptr})); + + return result; +} + +std::variant> +cudm_op_conversion::evaluate( + const std::variant> &op) { + if (std::holds_alternative(op)) { + const scalar_operator &scalar_op = std::get(op); + + ScalarCallbackFunction generator = scalar_op.get_generator(); + + if (!generator) { + return scalar_op.evaluate({}); + } else { + return _wrap_callback(scalar_op); + } + } + + if (std::holds_alternative(op)) { + const matrix_operator &mat_op = std::get(op); + + cudensitymatOperatorTerm_t opterm; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle_, dimensions_.size(), nullptr, &opterm)); + + cudensitymatElementaryOperator_t elem_op; + cudensitymatWrappedTensorCallback_t callback = + _wrap_callback_tensor(mat_op); + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle_, mat_op.degrees.size(), nullptr, + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, nullptr, + callback, &elem_op)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle_, opterm, 1, &elem_op, mat_op.degrees.data(), nullptr, + {1.0, 0.0}, {nullptr, nullptr})); + + return opterm; + } + + if (std::holds_alternative>(op)) { + throw std::runtime_error( + "Handling of product_operator is not implemented."); + } + + throw std::runtime_error( + "Unknown operator type in cudm_op_conversion::evaluate."); +} + +cudensitymatWrappedScalarCallback_t +cudm_op_conversion::_wrap_callback(const scalar_operator &scalar_op) { + try { + std::complex evaluatedValue = scalar_op.evaluate({}); + + cudensitymatWrappedScalarCallback_t wrapped_callback; + wrapped_callback.callback = nullptr; + wrapped_callback.wrapper = new std::complex(evaluatedValue); + return wrapped_callback; + } catch (const std::exception &) { + } + + ScalarCallbackFunction generator = scalar_op.get_generator(); + + if (!generator) { + throw std::runtime_error( + "scalar_operator does not have a valid generator function."); + } + + auto callback = [](double time, int32_t num_params, const double params[], + cudaDataType_t data_type, + void *scalar_storage) -> int32_t { + try { + scalar_operator *scalar_op = + static_cast(scalar_storage); + + std::map> param_map; + for (size_t i = 0; i < num_params; i++) { + param_map[std::to_string(i)] = params[i]; + } + + std::complex result = scalar_op->evaluate(param_map); + + if (data_type == CUDA_C_64F) { + *reinterpret_cast(scalar_storage) = + make_cuDoubleComplex(result.real(), result.imag()); + } else if (data_type == CUDA_C_32F) { + *reinterpret_cast(scalar_storage) = + make_cuFloatComplex(static_cast(result.real()), + static_cast(result.imag())); + } else { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in scalar callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedScalarCallback_t wrappedCallback; + wrappedCallback.callback = callback; + wrappedCallback.wrapper = new scalar_operator(scalar_op); + + return wrappedCallback; +} + +cudensitymatWrappedTensorCallback_t +cudm_op_conversion::_wrap_callback_tensor(const matrix_operator &op) { + auto callback = + [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, + const int64_t mode_extents[], const int32_t diagonal_offsets[], + double time, int32_t num_params, const double params[], + cudaDataType_t data_type, void *tensor_storage) -> int32_t { + try { + matrix_operator *mat_op = static_cast(tensor_storage); + + std::map> param_map; + for (size_t i = 0; i < num_params; i++) { + param_map[std::to_string(i)] = params[i]; + } + + matrix_2 matrix_data = mat_op->to_matrix({}, param_map); + + std::size_t rows = matrix_data.get_rows(); + std::size_t cols = matrix_data.get_columns(); + + if (num_modes != rows) { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + if (data_type == CUDA_C_64F) { + cuDoubleComplex *storage = + static_cast(tensor_storage); + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + storage[i * cols + j] = make_cuDoubleComplex( + matrix_data[{i, j}].real(), matrix_data[{i, j}].imag()); + } + } + } else if (data_type == CUDA_C_32F) { + cuFloatComplex *storage = static_cast(tensor_storage); + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + storage[i * cols + j] = make_cuFloatComplex( + static_cast(matrix_data[{i, j}].real()), + static_cast(matrix_data[{i, j}].imag())); + } + } + } else { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in tensor callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedTensorCallback_t wrapped_callback; + wrapped_callback.callback = callback; + wrapped_callback.wrapper = new matrix_operator(op); + + return wrapped_callback; +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 5371926a07..a7f2f052c9 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -65,6 +65,10 @@ std::complex scalar_operator::evaluate( return generator(parameters); } +ScalarCallbackFunction scalar_operator::get_generator() const { + return generator; +} + matrix_2 scalar_operator::to_matrix( const std::map dimensions, const std::map> parameters) const { diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index d818c55d68..baa9e7bee6 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -73,6 +73,8 @@ class scalar_operator { std::complex evaluate( const std::map> parameters = {}) const; + ScalarCallbackFunction get_generator() const; + // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. matrix_2 to_matrix( From 86292c78438d5995cab61fab0dccc03dcd3f967d Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 7 Feb 2025 02:08:42 +0000 Subject: [PATCH 220/311] Fix segfault and add an end-to-end stepper check Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_state.h | 4 ++ runtime/cudaq/dynamics/cudm_helpers.cpp | 37 +++++++++---------- runtime/cudaq/dynamics/cudm_state.cpp | 2 + unittests/dynamics/test_cudm_time_stepper.cpp | 25 +++++++++++++ 4 files changed, 48 insertions(+), 20 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 486e3034ac..227dcb63e4 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -72,6 +72,10 @@ class cudm_state { /// @return A copy of the raw data as a vector of complex numbers. std::vector> get_raw_data() const; + /// @brief Get the pointer to device memory buffer storing the state. + /// @return GPU device pointer + void *get_device_pointer() const; + /// @brief Get a copy of the hilbert space dimensions for the quantum state. /// @return A copy of the hilbert space dimensions of a vector of integers. std::vector get_hilbert_space_dims() const; diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 667c53bf52..5d8ab4d0c2 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -10,13 +10,13 @@ #include "cudaq/cudm_error_handling.h" namespace cudaq { -// Function to flatten a matrix into a 1D array +// Function to flatten a matrix into a 1D array (column major) std::vector> flatten_matrix(const matrix_2 &matrix) { std::vector> flat_matrix; - - for (size_t i = 0; i < matrix.get_rows(); i++) { - for (size_t j = 0; j < matrix.get_columns(); j++) { - flat_matrix.push_back(matrix[{i, j}]); + flat_matrix.reserve(matrix.get_size()); + for (size_t col = 0; col < matrix.get_columns(); col++) { + for (size_t row = 0; row < matrix.get_rows(); row++) { + flat_matrix.push_back(matrix[{row, col}]); } } @@ -53,19 +53,14 @@ cudensitymatElementaryOperator_t create_elementary_operator( cudensitymatElementaryOperator_t cudm_elem_op = nullptr; - std::vector interleaved_matrix; - interleaved_matrix.reserve(flat_matrix.size() * 2); - - for (const auto &value : flat_matrix) { - interleaved_matrix.push_back(value.real()); - interleaved_matrix.push_back(value.imag()); - } + // FIXME: leak (need to track this buffer somewhere and delete **after** the + // whole evolve) + auto *elementaryMat_d = create_array_gpu(flat_matrix); cudensitymatStatus_t status = cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, static_cast(interleaved_matrix.data()), - {nullptr, nullptr}, &cudm_elem_op); + CUDA_C_64F, elementaryMat_d, {nullptr, nullptr}, &cudm_elem_op); if (status != CUDENSITYMAT_STATUS_SUCCESS) { std::cerr << "Error: Failed to create elementary operator. Status: " @@ -92,7 +87,7 @@ void append_elementary_operator_to_term( std::vector elem_ops = {elem_op}; std::vector modeActionDuality(degrees.size(), 0); - + assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle, term, static_cast(degrees.size()), elem_ops.data(), degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), @@ -233,13 +228,15 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); + // FIXME: leak + // We must track these handles and destroy **after** evolve finishes // Destroy the term - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - // Cleanup - for (auto &elem_op : elementary_operators) { - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - } + // // Cleanup + // for (auto &elem_op : elementary_operators) { + // HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + // } } return operator_handle; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index c44bde5d2a..2da81e8c29 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -120,6 +120,8 @@ std::vector> cudm_state::get_raw_data() const { return rawData_; } +void *cudm_state::get_device_pointer() const { return gpuData_; } + std::vector cudm_state::get_hilbert_space_dims() const { return hilbertSpaceDims_; } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 1bfe69c79d..c84555ebed 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -91,3 +91,28 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepZeroStepSize) { TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { EXPECT_NO_THROW(time_stepper_->compute(*state_, 1e6, 1e3)); } + +TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { + const std::vector> initialState = { + {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + const std::vector dims = {4}; + auto inputState = std::make_unique(handle_, initialState, dims); + auto op = cudaq::matrix_operator::create(0); + auto cudmOp = cudaq::convert_to_cudensitymat_operator( + handle_, {}, op, dims); // Initialize the time stepper + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(*inputState, 0.0, 1.0); + + std::vector> outputStateVec(4); + HANDLE_CUDA_ERROR(cudaMemcpy( + outputStateVec.data(), outputState.get_device_pointer(), + outputStateVec.size() * sizeof(std::complex), cudaMemcpyDefault)); + // Create operator move the state up 1 step. + const std::vector> expectedOutputState = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + + for (std::size_t i = 0; i < expectedOutputState.size(); ++i) { + EXPECT_TRUE(std::abs(expectedOutputState[i] - outputStateVec[i]) < 1e-12); + } + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); +} From 28331240fedd67e72e4379f44f57832ea4961bf2 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 7 Feb 2025 05:12:36 +0000 Subject: [PATCH 221/311] Fix RK 1st order and add a test Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_state.h | 5 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 10 ++- runtime/cudaq/dynamics/cudm_state.cpp | 39 ++++++++---- .../cudaq/dynamics/runge_kutta_integrator.cpp | 35 ++++++----- .../dynamics/test_runge_kutta_integrator.cpp | 61 +++++++++++++++++++ 5 files changed, 121 insertions(+), 29 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 227dcb63e4..4e1a0d2180 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -60,6 +60,9 @@ class cudm_state { /// @return String representation of the state data. std::string dump() const; + /// @brief Dump the state data to the console for debugging purposes. + void dumpDeviceData() const; + /// @brief Convert the state vector to a density matrix. /// @return A new cudm_state representing the density matrix. cudm_state to_density_matrix() const; @@ -94,7 +97,7 @@ class cudm_state { /// @brief Scalar multiplication operator /// @return The new state after multiplying scalar with the current state. - cudm_state operator*(double scalar) const; + cudm_state &operator*=(const std::complex &scalar); private: std::vector> rawData_; diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 5d8ab4d0c2..c48125b33e 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -218,14 +218,22 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( } else if (const auto *scalar_op = dynamic_cast( &component)) { + // FIXME: do we need this code path? + // The product_op already has get_coefficient method. auto coeff = scalar_op->evaluate(parameters); append_scalar_to_term(handle, term, coeff); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); } } + // Handle the coefficient + auto coeff = product_op.get_coefficient().evaluate(parameters); // Append the product operator term to the top-level operator HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + handle, operator_handle, term, 0, + make_cuDoubleComplex(coeff.real(), coeff.imag()), {nullptr, nullptr})); // FIXME: leak diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 2da81e8c29..7cd0bdf127 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -167,20 +167,18 @@ cudm_state &cudm_state::operator+=(const cudm_state &other) { return *this; } +cudm_state &cudm_state::operator*=(const std::complex &scalar) { + void *gpuScalar; + HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &scalar, sizeof(std::complex), + cudaMemcpyHostToDevice)); -cudm_state cudm_state::operator*(double scalar) const { - cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); - - double *gpuScalar; - cudaMalloc(reinterpret_cast(&gpuScalar), sizeof(double)); - cudaMemcpy(gpuScalar, &scalar, sizeof(double), cudaMemcpyHostToDevice); - - HANDLE_CUDM_ERROR(cudensitymatStateComputeScaling(handle_, result.get_impl(), - gpuScalar, 0)); + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(handle_, state_, gpuScalar, 0)); - cudaFree(gpuScalar); + HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); - return result; + return *this; } std::string cudm_state::dump() const { @@ -200,6 +198,25 @@ std::string cudm_state::dump() const { return oss.str(); } +void cudm_state::dumpDeviceData() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + std::vector> hostBuffer(rawData_.size()); + HANDLE_CUDA_ERROR(cudaMemcpy(hostBuffer.data(), get_device_pointer(), + hostBuffer.size() * sizeof(std::complex), + cudaMemcpyDefault)); + std::cout << "State data: ["; + for (size_t i = 0; i < hostBuffer.size(); i++) { + std::cout << hostBuffer[i]; + if (i < hostBuffer.size() - 1) { + std::cout << ", "; + } + } + std::cout << "]\n"; +} + cudm_state cudm_state::to_density_matrix() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 87b7f48819..56441cea09 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -30,31 +30,34 @@ void runge_kutta_integrator::integrate(double target_time) { while (this->t < target_time) { double step_size = std::min(dt, target_time - this->t); - std::cout << "Runge-Kutta step at time " << this->t - << " with step size: " << step_size << std::endl; + // std::cout << "Runge-Kutta step at time " << this->t + // << " with step size: " << step_size << std::endl; if (this->substeps_ == 1) { // Euler method (1st order) cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= step_size; this->state += k1; } else if (this->substeps_ == 2) { + // FIXME: implement it // Midpoint method (2nd order) - cudm_state k1 = - this->stepper->compute(this->state, this->t, step_size / 2.0); - cudm_state k2 = - this->stepper->compute(k1, this->t + step_size / 2.0, step_size); - this->state += (k1 + k2) * 0.5; + // cudm_state k1 = + // this->stepper->compute(this->state, this->t, step_size / 2.0); + // cudm_state k2 = + // this->stepper->compute(k1, this->t + step_size / 2.0, step_size); + // this->state += (k1 + k2) * 0.5; } else if (this->substeps_ == 4) { + // FIXME: implement it // Runge-Kutta method (4th order) - cudm_state k1 = - this->stepper->compute(this->state, this->t, step_size / 2.0); - cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, - step_size / 2.0); - cudm_state k3 = - this->stepper->compute(k2, this->t + step_size / 2.0, step_size); - cudm_state k4 = - this->stepper->compute(k3, this->t + step_size, step_size); - this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); + // cudm_state k1 = + // this->stepper->compute(this->state, this->t, step_size / 2.0); + // cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, + // step_size / 2.0); + // cudm_state k3 = + // this->stepper->compute(k2, this->t + step_size / 2.0, step_size); + // cudm_state k4 = + // this->stepper->compute(k3, this->t + step_size, step_size); + // this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } // Update time diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 9541316100..48a5852629 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -147,3 +147,64 @@ TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { 0.0, time_stepper_, 3), std::invalid_argument); } + +TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { + const std::vector> initialState = {{1.0, 0.0}, + {0.0, 0.0}}; + const std::vector dims = {2}; + const std::string op_id = "pauli_x"; + auto func = [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = matrix_2(2, 2); + mat[{1, 0}] = 1.0; + mat[{0, 1}] = 1.0; + return mat; + }; + cudaq::matrix_operator::define(op_id, {-1}, func); + + // FIXME: enable all orders + // for (int integratorOrder : {1, 2, 4}) { + for (int integratorOrder : {1}) { + std::cout << "Test RK order " << integratorOrder << "\n"; + auto op = cudaq::product_operator( + std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, + cudaq::matrix_operator(op_id, {0})); + auto cudmOp = + cudaq::convert_to_cudensitymat_operator( + handle_, {}, op, dims); + auto time_stepper = std::make_shared(handle_, cudmOp); + + auto eulerIntegrator = std::make_unique( + cudm_state(handle_, initialState, dims), 0.0, time_stepper, + integratorOrder); + eulerIntegrator->set_option("dt", 0.001); + + constexpr std::size_t numDataPoints = 10; + double t = 0.0; + std::vector> outputStateVec(2); + for (std::size_t i = 1; i < numDataPoints; ++i) { + eulerIntegrator->integrate(i); + auto [t, state] = eulerIntegrator->get_state(); + // std::cout << "Time = " << t << "\n"; + // state.dumpDeviceData(); + HANDLE_CUDA_ERROR( + cudaMemcpy(outputStateVec.data(), state.get_device_pointer(), + outputStateVec.size() * sizeof(std::complex), + cudaMemcpyDefault)); + // Check state vector norm + EXPECT_NEAR(std::norm(outputStateVec[0]) + std::norm(outputStateVec[1]), + 1.0, 1e-2); + const double expValZ = + std::norm(outputStateVec[0]) - std::norm(outputStateVec[1]); + // Analytical results + EXPECT_NEAR(outputStateVec[0].real(), std::cos(2.0 * M_PI * 0.1 * t), + 1e-2); + } + + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); + } +} From 4f33f6476a5d74dc6532814dc5a310b12762f244 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 6 Feb 2025 21:44:47 -0800 Subject: [PATCH 222/311] Adding unittests for cudm_op_conversion Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 2 + runtime/cudaq/cudm_op_conversion.h | 8 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 28 ++++ runtime/cudaq/dynamics/cudm_op_conversion.cpp | 70 ++++++++-- runtime/cudaq/dynamics/cudm_state.cpp | 2 +- unittests/CMakeLists.txt | 1 + unittests/dynamics/test_cudm_helpers.cpp | 2 +- .../dynamics/test_cudm_op_conversion.cpp | 129 ++++++++++++++++++ unittests/dynamics/test_mocks.h | 7 + 9 files changed, 229 insertions(+), 20 deletions(-) create mode 100644 unittests/dynamics/test_cudm_op_conversion.cpp diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index ede4ee0243..ff56ea0541 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -17,6 +17,8 @@ #include namespace cudaq { +std::vector> flatten_matrix(const matrix_2 &matrix); + void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, double scale_factor, cudaStream_t stream); diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 50395f0c56..c37c7b7cb3 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -39,11 +39,13 @@ class cudm_op_conversion { // Addition of two operator terms std::variant + std::complex> add(const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2); // Evaluate an operator and convert it to cudensitymatOperatorTerm_t std::variant> flatten_matrix(const matrix_2 &matrix) { @@ -51,6 +53,9 @@ cudensitymatElementaryOperator_t create_elementary_operator( throw std::invalid_argument("subspace_extents cannot be empty."); } + std::cout << "Subspace extents size: " << subspace_extents.size() + << ", Matrix size: " << flat_matrix.size() << std::endl; + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; // FIXME: leak (need to track this buffer somewhere and delete **after** the @@ -84,8 +89,20 @@ void append_elementary_operator_to_term( throw std::invalid_argument("elem_op cannot be null."); } + std::cout << "Appending Elementary Operator to Term" + << " - Degrees: " << degrees.size() << " - Term: " << term + << std::endl; + + for (int degree : degrees) { + if (degree < 0) { + throw std::out_of_range("Degree cannot be negative!"); + } + } + std::vector elem_ops = {elem_op}; + std::cout << "elem_ops.data(): " << elem_ops.data() << std::endl; + std::vector modeActionDuality(degrees.size(), 0); assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( @@ -111,8 +128,19 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, throw std::invalid_argument("Invalid state provided to scale_state."); } + cudaEvent_t start, stop; + cudaEventCreate(&start); + cudaEventCreate(&stop); + cudaEventRecord(start, stream); + HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); + + cudaEventRecord(stop, stream); + cudaEventSynchronize(stop); + float milliseconds = 0; + cudaEventElapsedTime(&milliseconds, start, stop); + std::cout << "Time taken: " << milliseconds << " ms" << std::endl; } cudensitymatOperator_t diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index b16f4231f4..c60179975d 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -18,7 +18,16 @@ namespace cudaq { cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, const std::map &dimensions, std::shared_ptr schedule) - : handle_(handle), dimensions_(dimensions), schedule_(schedule) {} + : handle_(handle), dimensions_(dimensions), schedule_(schedule) { + std::cout << "Handle: " << handle_ << std::endl; + if (handle_ == nullptr) { + throw std::runtime_error("Handle cannot be null."); + } + + if (dimensions_.empty()) { + throw std::invalid_argument("Dimensions map must not be empty."); + } +} cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( const cudensitymatWrappedScalarCallback_t &scalar) { @@ -91,19 +100,37 @@ cudm_op_conversion::mul( } std::variant -cudm_op_conversion::add( - const std::variant &op1, - const std::variant &op2) { - if (std::holds_alternative(op1) || - std::holds_alternative(op2)) { - return std::get(op1) + std::get(op2); + std::complex> +cudm_op_conversion::add(const std::variant> &op1, + const std::variant> &op2) { + if (std::holds_alternative>(op1) && + std::holds_alternative>(op2)) { + return std::get>(op1) + + std::get>(op2); } + if (std::holds_alternative>(op1)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op1))), + std::get(op2)); + } + + if (std::holds_alternative>(op2)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op2))), + std::get(op1)); + } + + // FIXME: Need to check later + int32_t num_space_modes = + std::max(static_cast(dimensions_.size()), 1); + cudensitymatOperatorTerm_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, num_space_modes, nullptr, &result)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( @@ -136,18 +163,31 @@ cudm_op_conversion::evaluate( if (std::holds_alternative(op)) { const matrix_operator &mat_op = std::get(op); + std::vector space_mode_extents; + for (const auto &dim : dimensions_) { + space_mode_extents.push_back(dim.second); + } + cudensitymatOperatorTerm_t opterm; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle_, dimensions_.size(), nullptr, &opterm)); + handle_, dimensions_.size(), space_mode_extents.data(), &opterm)); cudensitymatElementaryOperator_t elem_op; cudensitymatWrappedTensorCallback_t callback = _wrap_callback_tensor(mat_op); + auto flat_matrix = flatten_matrix(mat_op.to_matrix(dimensions_, {})); + + void *tensor_data = create_array_gpu(flat_matrix); + if (!tensor_data) { + throw std::runtime_error( + "Failed to allocate GPU memory for tensor_data."); + } + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle_, mat_op.degrees.size(), nullptr, - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, nullptr, - callback, &elem_op)); + handle_, mat_op.degrees.size(), space_mode_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, + tensor_data, callback, &elem_op)); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle_, opterm, 1, &elem_op, mat_op.degrees.data(), nullptr, diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 7cd0bdf127..72901abba1 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -28,7 +28,7 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, // Allocate device memory size_t dataSize = rawData_.size() * sizeof(std::complex); - cudaMalloc(reinterpret_cast(&gpuData_), dataSize); + HANDLE_CUDA_ERROR(cudaMalloc(reinterpret_cast(&gpuData_), dataSize)); // Copy data from host to device HANDLE_CUDA_ERROR( diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index a5db4a6718..99160e52f1 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -295,6 +295,7 @@ if (CUDA_FOUND) dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp + dynamics/test_cudm_op_conversion.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index c5d5e9759c..aed09951a7 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -50,7 +50,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { ASSERT_TRUE(state.is_initialized()); - EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), 2.0, stream)); + EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), {2.0}, stream)); } // Test for compute_lindblad_op diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp new file mode 100644 index 0000000000..122f68993d --- /dev/null +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include "cudaq/cudm_op_conversion.h" +#include "cudaq/operators.h" +#include "test_mocks.h" +#include +#include +#include + +using namespace cudaq; + +class CuDmOpConversion : public ::testing::Test { +protected: + cudensitymatHandle_t handle; + std::map dimensions; + std::shared_ptr schedule; + std::unique_ptr converter; + std::vector space_mode_extents; + + void SetUp() override { + handle = mock_handle(); + dimensions = {{0, 2}, {1, 2}}; + for (const auto &dim : dimensions) { + space_mode_extents.push_back(dim.second); + } + schedule = std::shared_ptr(); + converter = std::make_unique(handle, dimensions, schedule); + } +}; + +TEST_F(CuDmOpConversion, ConstructorValid) { + EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); +} + +TEST_F(CuDmOpConversion, ConstructorEmptyDimensions) { + std::map empty_dimensions; + EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), std::invalid_argument); +} + +TEST_F(CuDmOpConversion, ConstructorInvalidHandle) { + cudensitymatHandle_t invalid_handle = nullptr; + EXPECT_THROW(cudm_op_conversion converter(invalid_handle, dimensions, schedule), std::runtime_error); +} + +TEST_F(CuDmOpConversion, EvaluateScalarConstant) { + scalar_operator scalar_op(2.5); + auto result = converter->evaluate(scalar_op); + + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), std::complex(2.5, 0.0)); +} + +TEST_F(CuDmOpConversion, EvaluateScalarCallback) { + scalar_operator scalar_op([](std::map>) { + return std::complex(1.0, -1.0); + }); + auto result = converter->evaluate(scalar_op); + + ASSERT_TRUE(std::holds_alternative(result)); +} + +// TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { +// matrix_operator mat_op("H", {0}); +// auto result = converter->evaluate(mat_op); + +// ASSERT_TRUE(std::holds_alternative(result)); +// } + +TEST_F(CuDmOpConversion, EvaluateProductOperator) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(0); + product_operator product_op = op0 * op1; + EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); +} + +TEST_F(CuDmOpConversion, AddOperators) { + scalar_operator scalar_op1(2.0); + scalar_operator scalar_op2(3.0); + + auto result = converter->add(converter->evaluate(scalar_op1), converter->evaluate(scalar_op2)); + + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); +} + +TEST_F(CuDmOpConversion, AddComplexScalars) { + std::complex scalar_1(2.0, 1.0); + std::complex scalar_2(3.0, -1.0); + + auto result = converter->add(scalar_1, scalar_2); + + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); +} + +// TEST_F(CuDmOpConversion, AddScalarAndOperator) { +// scalar_operator scalar_op(1.0); +// matrix_operator mat_op("X", {0}); + +// auto scalar_result = converter->evaluate(scalar_op); +// auto op_result = converter->evaluate(mat_op); + +// auto final_result = converter->add(scalar_result, op_result); + +// ASSERT_TRUE(std::holds_alternative(final_result)); +// } + +TEST_F(CuDmOpConversion, TensorProductOfScalars) { + auto result = converter->tensor(2.0, 3.0); + EXPECT_TRUE(std::holds_alternative(result)); + EXPECT_EQ(std::get(result), 6.0); +} + +// TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { +// cudensitymatOperatorTerm_t op_term; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, dimensions.size(), space_mode_extents.data(), &op_term)); + +// auto result = converter->tensor(2.0, op_term); +// EXPECT_TRUE(std::holds_alternative(result)); + +// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); +// } diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 7715b14a7d..801e0d2541 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -14,6 +14,13 @@ #include #include +// Mock cudensitymatHandle_t +inline cudensitymatHandle_t mock_handle() { + cudensitymatHandle_t handle; + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + return handle; +} + // Mock Liouvillian operator creation inline cudensitymatOperator_t mock_liouvillian(cudensitymatHandle_t handle) { cudensitymatOperator_t liouvillian = nullptr; From ea79f4c5c8ce4cb34042eb05dc9c93bb23340a58 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 13:48:31 -0800 Subject: [PATCH 223/311] Adding operator* method Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 4 ++++ runtime/cudaq/dynamics/cudm_state.cpp | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 4e1a0d2180..a1b84148ca 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -99,6 +99,10 @@ class cudm_state { /// @return The new state after multiplying scalar with the current state. cudm_state &operator*=(const std::complex &scalar); + cudm_state operator*(double scalar) &&; + + double norm() const; + private: std::vector> rawData_; std::complex *gpuData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 72901abba1..f863616b37 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -181,6 +181,21 @@ cudm_state &cudm_state::operator*=(const std::complex &scalar) { return *this; } +cudm_state cudm_state::operator*(double scalar) && { + this->operator*=(scalar); + return std::move(*this); +} + +double cudm_state::norm() const { + std::vector> rawData = this->get_raw_data(); + + double norm = 0.0; + for (const auto &val : rawData) { + norm += std::norm(val); + } + return std::sqrt(norm); +} + std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From dfbf83f73a2eae79a4ea979f1d930f8856d08d81 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 13:57:05 -0800 Subject: [PATCH 224/311] Removing norm method from state Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 2 -- runtime/cudaq/dynamics/cudm_state.cpp | 10 ---------- 2 files changed, 12 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index a1b84148ca..1c7082cdfe 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -101,8 +101,6 @@ class cudm_state { cudm_state operator*(double scalar) &&; - double norm() const; - private: std::vector> rawData_; std::complex *gpuData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index f863616b37..3b837101d3 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -186,16 +186,6 @@ cudm_state cudm_state::operator*(double scalar) && { return std::move(*this); } -double cudm_state::norm() const { - std::vector> rawData = this->get_raw_data(); - - double norm = 0.0; - for (const auto &val : rawData) { - norm += std::norm(val); - } - return std::sqrt(norm); -} - std::string cudm_state::dump() const { if (!is_initialized()) { throw std::runtime_error("State is not initialized."); From f2ec850cb66b7e23360469216fc07ed0f1e04bc1 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 15:02:50 -0800 Subject: [PATCH 225/311] Fixing midpoint integration Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 21 +++++++++++++------ .../dynamics/test_runge_kutta_integrator.cpp | 4 ++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 56441cea09..62c5a46390 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -39,13 +39,17 @@ void runge_kutta_integrator::integrate(double target_time) { k1 *= step_size; this->state += k1; } else if (this->substeps_ == 2) { - // FIXME: implement it // Midpoint method (2nd order) - // cudm_state k1 = - // this->stepper->compute(this->state, this->t, step_size / 2.0); - // cudm_state k2 = - // this->stepper->compute(k1, this->t + step_size / 2.0, step_size); - // this->state += (k1 + k2) * 0.5; + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= (step_size / 2.0); + + this->state += k1; + + cudm_state k2 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k2 *= (step_size / 2.0); + + this->state += k2; } else if (this->substeps_ == 4) { // FIXME: implement it // Runge-Kutta method (4th order) @@ -60,6 +64,11 @@ void runge_kutta_integrator::integrate(double target_time) { // this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); } + // double norm_factor = this->state.norm(); + // if (norm_factor > 1e-8) { + // this->state *= (1.0 / norm_factor); + // } + // Update time this->t += step_size; } diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 48a5852629..fce0fef120 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -168,7 +168,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { // FIXME: enable all orders // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1}) { + for (int integratorOrder : {1, 2}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, @@ -183,7 +183,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { integratorOrder); eulerIntegrator->set_option("dt", 0.001); - constexpr std::size_t numDataPoints = 10; + constexpr std::size_t numDataPoints = 2; double t = 0.0; std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { From bb0baff699ba8fad74633eaff80dd24ab46d4811 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 15:56:31 -0800 Subject: [PATCH 226/311] Setting the accumulation for 4 states Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 36 +++++++++++-------- .../dynamics/test_runge_kutta_integrator.cpp | 4 +-- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 62c5a46390..66147ac559 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -53,21 +53,29 @@ void runge_kutta_integrator::integrate(double target_time) { } else if (this->substeps_ == 4) { // FIXME: implement it // Runge-Kutta method (4th order) - // cudm_state k1 = - // this->stepper->compute(this->state, this->t, step_size / 2.0); - // cudm_state k2 = this->stepper->compute(k1, this->t + step_size / 2.0, - // step_size / 2.0); - // cudm_state k3 = - // this->stepper->compute(k2, this->t + step_size / 2.0, step_size); - // cudm_state k4 = - // this->stepper->compute(k3, this->t + step_size, step_size); - // this->state += (k1 + (k2 + k3) * 2.0 + k4) * (1.0 / 6.0); - } + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= (step_size / 2.0); + + this->state += k1; + + cudm_state k2 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k2 *= (step_size / 2.0); + + this->state += k2; + + cudm_state k3 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k3 *= (step_size / 2.0); + + this->state += k3; - // double norm_factor = this->state.norm(); - // if (norm_factor > 1e-8) { - // this->state *= (1.0 / norm_factor); - // } + cudm_state k4 = + this->stepper->compute(this->state, this->t + step_size, step_size); + k4 *= (step_size / 2.0); + + this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); + } // Update time this->t += step_size; diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index fce0fef120..803c969a65 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -168,7 +168,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { // FIXME: enable all orders // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1, 2}) { + for (int integratorOrder : {1, 2, 4}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, @@ -183,7 +183,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { integratorOrder); eulerIntegrator->set_option("dt", 0.001); - constexpr std::size_t numDataPoints = 2; + constexpr std::size_t numDataPoints = 10; double t = 0.0; std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { From 1c798e1072fdda7d4132ace2ea6b484215f29c5a Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 20:07:41 -0800 Subject: [PATCH 227/311] Disabling RK4 for now Signed-off-by: Sachin Pisal --- .../cudaq/dynamics/runge_kutta_integrator.cpp | 31 ++++++++++--------- .../dynamics/test_runge_kutta_integrator.cpp | 2 +- 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 66147ac559..04ae89e595 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -53,28 +53,29 @@ void runge_kutta_integrator::integrate(double target_time) { } else if (this->substeps_ == 4) { // FIXME: implement it // Runge-Kutta method (4th order) - cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); - k1 *= (step_size / 2.0); + // cudm_state k1 = this->stepper->compute(this->state, this->t, + // step_size); k1 *= (step_size / 2.0); - this->state += k1; + // this->state += k1; - cudm_state k2 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k2 *= (step_size / 2.0); + // cudm_state k2 = this->stepper->compute( + // this->state, this->t + step_size / 2.0, step_size); + // k2 *= (step_size / 2.0); - this->state += k2; + // this->state += k2; - cudm_state k3 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k3 *= (step_size / 2.0); + // cudm_state k3 = this->stepper->compute( + // this->state, this->t + step_size / 2.0, step_size); + // k3 *= step_size; - this->state += k3; + // this->state += k3; - cudm_state k4 = - this->stepper->compute(this->state, this->t + step_size, step_size); - k4 *= (step_size / 2.0); + // cudm_state k4 = + // this->stepper->compute(this->state, this->t + step_size, + // step_size); + // k4 *= step_size; - this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); + // this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); } // Update time diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 803c969a65..4de2dd526d 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -168,7 +168,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { // FIXME: enable all orders // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1, 2, 4}) { + for (int integratorOrder : {1, 2}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, From 9911353abf3d459ec65635428a90d2017d49bbbe Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 7 Feb 2025 21:29:36 -0800 Subject: [PATCH 228/311] * Adding mode_action_duality * Adding mock function to create a matrix operator * Adding unittests for mul * Formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 11 +- .../dynamics/test_cudm_op_conversion.cpp | 139 +++++++++++------- unittests/dynamics/test_mocks.h | 25 ++++ 3 files changed, 117 insertions(+), 58 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index c60179975d..3d069b7d08 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -184,14 +184,17 @@ cudm_op_conversion::evaluate( "Failed to allocate GPU memory for tensor_data."); } + std::vector mode_action_duality( + mat_op.degrees.size(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE); + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( handle_, mat_op.degrees.size(), space_mode_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, - tensor_data, callback, &elem_op)); + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), + CUDA_C_64F, tensor_data, callback, &elem_op)); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle_, opterm, 1, &elem_op, mat_op.degrees.data(), nullptr, - {1.0, 0.0}, {nullptr, nullptr})); + handle_, opterm, 1, &elem_op, mat_op.degrees.data(), + mode_action_duality.data(), {1.0, 0.0}, {nullptr, nullptr})); return opterm; } diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index 122f68993d..0b112a7f03 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -6,103 +6,112 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include #include "cudaq/cudm_op_conversion.h" #include "cudaq/operators.h" #include "test_mocks.h" -#include #include +#include #include +#include using namespace cudaq; class CuDmOpConversion : public ::testing::Test { protected: - cudensitymatHandle_t handle; - std::map dimensions; - std::shared_ptr schedule; - std::unique_ptr converter; - std::vector space_mode_extents; - - void SetUp() override { - handle = mock_handle(); - dimensions = {{0, 2}, {1, 2}}; - for (const auto &dim : dimensions) { - space_mode_extents.push_back(dim.second); - } - schedule = std::shared_ptr(); - converter = std::make_unique(handle, dimensions, schedule); + cudensitymatHandle_t handle; + std::map dimensions; + std::shared_ptr schedule; + std::unique_ptr converter; + std::vector space_mode_extents; + + void SetUp() override { + handle = mock_handle(); + dimensions = {{0, 2}, {1, 2}}; + for (const auto &dim : dimensions) { + space_mode_extents.push_back(dim.second); } + schedule = std::shared_ptr(); + converter = + std::make_unique(handle, dimensions, schedule); + } }; TEST_F(CuDmOpConversion, ConstructorValid) { - EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); + EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); } TEST_F(CuDmOpConversion, ConstructorEmptyDimensions) { - std::map empty_dimensions; - EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), std::invalid_argument); + std::map empty_dimensions; + EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), + std::invalid_argument); } TEST_F(CuDmOpConversion, ConstructorInvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; - EXPECT_THROW(cudm_op_conversion converter(invalid_handle, dimensions, schedule), std::runtime_error); + cudensitymatHandle_t invalid_handle = nullptr; + EXPECT_THROW( + cudm_op_conversion converter(invalid_handle, dimensions, schedule), + std::runtime_error); } TEST_F(CuDmOpConversion, EvaluateScalarConstant) { - scalar_operator scalar_op(2.5); - auto result = converter->evaluate(scalar_op); + scalar_operator scalar_op(2.5); + auto result = converter->evaluate(scalar_op); - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), std::complex(2.5, 0.0)); + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), + std::complex(2.5, 0.0)); } TEST_F(CuDmOpConversion, EvaluateScalarCallback) { - scalar_operator scalar_op([](std::map>) { - return std::complex(1.0, -1.0); - }); - auto result = converter->evaluate(scalar_op); + scalar_operator scalar_op([](std::map>) { + return std::complex(1.0, -1.0); + }); + auto result = converter->evaluate(scalar_op); - ASSERT_TRUE(std::holds_alternative(result)); + ASSERT_TRUE( + std::holds_alternative(result)); } -// TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { -// matrix_operator mat_op("H", {0}); -// auto result = converter->evaluate(mat_op); +TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { + matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); + auto result = converter->evaluate(mat_op); -// ASSERT_TRUE(std::holds_alternative(result)); -// } + ASSERT_TRUE(std::holds_alternative(result)); +} TEST_F(CuDmOpConversion, EvaluateProductOperator) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(0); - product_operator product_op = op0 * op1; - EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(0); + product_operator product_op = op0 * op1; + EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); } TEST_F(CuDmOpConversion, AddOperators) { - scalar_operator scalar_op1(2.0); - scalar_operator scalar_op2(3.0); + scalar_operator scalar_op1(2.0); + scalar_operator scalar_op2(3.0); - auto result = converter->add(converter->evaluate(scalar_op1), converter->evaluate(scalar_op2)); + auto result = converter->add(converter->evaluate(scalar_op1), + converter->evaluate(scalar_op2)); - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), + std::complex(5.0, 0.0)); } TEST_F(CuDmOpConversion, AddComplexScalars) { - std::complex scalar_1(2.0, 1.0); - std::complex scalar_2(3.0, -1.0); + std::complex scalar_1(2.0, 1.0); + std::complex scalar_2(3.0, -1.0); - auto result = converter->add(scalar_1, scalar_2); + auto result = converter->add(scalar_1, scalar_2); - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), std::complex(5.0, 0.0)); + ASSERT_TRUE(std::holds_alternative>(result)); + EXPECT_EQ(std::get>(result), + std::complex(5.0, 0.0)); } // TEST_F(CuDmOpConversion, AddScalarAndOperator) { // scalar_operator scalar_op(1.0); -// matrix_operator mat_op("X", {0}); +// matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); // auto scalar_result = converter->evaluate(scalar_op); // auto op_result = converter->evaluate(mat_op); @@ -113,17 +122,39 @@ TEST_F(CuDmOpConversion, AddComplexScalars) { // } TEST_F(CuDmOpConversion, TensorProductOfScalars) { - auto result = converter->tensor(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative(result)); - EXPECT_EQ(std::get(result), 6.0); + auto result = converter->tensor(2.0, 3.0); + EXPECT_TRUE(std::holds_alternative(result)); + EXPECT_EQ(std::get(result), 6.0); } // TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { // cudensitymatOperatorTerm_t op_term; -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, dimensions.size(), space_mode_extents.data(), &op_term)); +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, +// dimensions.size(), space_mode_extents.data(), &op_term)); // auto result = converter->tensor(2.0, op_term); // EXPECT_TRUE(std::holds_alternative(result)); // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); // } + +TEST_F(CuDmOpConversion, MultiplyOperators) { + auto result = converter->mul(2.0, 3.0); + EXPECT_TRUE(std::holds_alternative(result)); + EXPECT_EQ(std::get(result), 6.0); +} + +TEST_F(CuDmOpConversion, MoveSemantics) { + scalar_operator scalar_op(1.5); + auto scalar_result = converter->evaluate(scalar_op); + + EXPECT_TRUE(std::holds_alternative>(scalar_result)); + + auto moved_result = std::move(scalar_result); + + EXPECT_TRUE(std::holds_alternative>(moved_result)); + EXPECT_EQ(std::get>(moved_result), + std::complex(1.5, 0.0)); + + EXPECT_TRUE(scalar_result.index() != std::variant_npos); +} diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 801e0d2541..dcb7a20bf0 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -8,12 +8,37 @@ #pragma once +#include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" #include #include #include #include #include +// Mock matrix_operator +inline cudaq::matrix_operator mock_matrix_operator(const std::string &op_id, + int qubit_index) { + try { + auto callback = [](std::vector dimensions, + std::map>) { + if (dimensions.size() != 1 || dimensions[0] != 2) { + throw std::invalid_argument("Invalid dimensions for operator."); + } + + cudaq::matrix_2 matrix(2, 2); + matrix[{0, 1}] = 1.0; + matrix[{1, 0}] = 1.0; + return matrix; + }; + + cudaq::matrix_operator::define(op_id, {-1}, callback); + } catch (...) { + } + + return cudaq::matrix_operator(op_id, {qubit_index}); +} + // Mock cudensitymatHandle_t inline cudensitymatHandle_t mock_handle() { cudensitymatHandle_t handle; From eb48e97a068873295e7f2bb6db1911df479592e7 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 8 Feb 2025 17:14:00 -0800 Subject: [PATCH 229/311] Fixing AddComplexScalars unittest Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 2 + runtime/cudaq/dynamics/cudm_op_conversion.cpp | 63 ++++++++++++++++--- .../dynamics/test_cudm_op_conversion.cpp | 16 ++--- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index c37c7b7cb3..d69d7266c8 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -66,5 +66,7 @@ class cudm_op_conversion { cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &op); cudensitymatWrappedTensorCallback_t _wrap_callback_tensor(const matrix_operator &op); + + std::vector> get_identity_matrix(); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 3d069b7d08..4a31d41b29 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -19,7 +19,6 @@ cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, const std::map &dimensions, std::shared_ptr schedule) : handle_(handle), dimensions_(dimensions), schedule_(schedule) { - std::cout << "Handle: " << handle_ << std::endl; if (handle_ == nullptr) { throw std::runtime_error("Handle cannot be null."); } @@ -29,19 +28,48 @@ cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, } } +std::vector> cudm_op_conversion::get_identity_matrix() { + std::vector> identity_matrix( + dimensions_.size() * dimensions_.size(), {0.0, 0.0}); + for (size_t i = 0; i < dimensions_.size(); i++) { + identity_matrix[i * dimensions_.size() + i] = {1.0, 0.0}; + } + + return identity_matrix; +} + cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( const cudensitymatWrappedScalarCallback_t &scalar) { cudensitymatOperatorTerm_t op_term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), - nullptr, &op_term)); + + std::vector space_mode_extents; + for (const auto &dim : dimensions_) { + space_mode_extents.push_back(dim.second); + } + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); + + void *tensor_data = create_array_gpu(get_identity_matrix()); + if (!tensor_data) { + throw std::runtime_error("Failed to allocate GPU memory for tensor_data."); + } + + std::vector mode_action_duality(dimensions_.size(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE); cudensitymatElementaryOperator_t identity; HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle_, 1, nullptr, CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, nullptr, {nullptr, nullptr}, &identity)); + handle_, dimensions_.size(), space_mode_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), + CUDA_C_64F, tensor_data, {nullptr, nullptr}, &identity)); + + std::vector states_modes_acted_on(dimensions_.size()); + std::iota(states_modes_acted_on.begin(), states_modes_acted_on.end(), 0); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle_, op_term, 1, &identity, nullptr, nullptr, {1.0, 0.0}, scalar)); + handle_, op_term, 1, &identity, states_modes_acted_on.data(), + mode_action_duality.data(), {1.0, 0.0}, scalar)); return op_term; } @@ -53,12 +81,27 @@ cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( throw std::invalid_argument("Invalid operator term (nullptr)."); } + std::vector space_mode_extents; + for (const auto &dim : dimensions_) { + space_mode_extents.push_back(dim.second); + } + + cudensitymatOperatorTerm_t scalar_term = _scalar_to_op(scalar); + cudensitymatOperatorTerm_t new_opterm; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), - nullptr, &new_opterm)); + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle_, static_cast(dimensions_.size()), + space_mode_extents.data(), &new_opterm)); + + std::vector mode_action_duality(dimensions_.size(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_opterm, op, 0, - {1.0, 0.0}, scalar)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, new_opterm, scalar_term, mode_action_duality.size(), {1.0, 0.0}, + scalar)); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle_, new_opterm, op, mode_action_duality.size(), {1.0, 0.0}, scalar)); return new_opterm; } diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index 0b112a7f03..a59c8d41ac 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -109,17 +109,17 @@ TEST_F(CuDmOpConversion, AddComplexScalars) { std::complex(5.0, 0.0)); } -// TEST_F(CuDmOpConversion, AddScalarAndOperator) { -// scalar_operator scalar_op(1.0); -// matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); +TEST_F(CuDmOpConversion, AddScalarAndOperator) { + scalar_operator scalar_op(1.0); + matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); -// auto scalar_result = converter->evaluate(scalar_op); -// auto op_result = converter->evaluate(mat_op); + auto scalar_result = converter->evaluate(scalar_op); + auto op_result = converter->evaluate(mat_op); -// auto final_result = converter->add(scalar_result, op_result); + auto final_result = converter->add(scalar_result, op_result); -// ASSERT_TRUE(std::holds_alternative(final_result)); -// } + ASSERT_TRUE(std::holds_alternative(final_result)); +} TEST_F(CuDmOpConversion, TensorProductOfScalars) { auto result = converter->tensor(2.0, 3.0); From 4a70b23921a7adb47e1ab27833a448dfb017cf65 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sat, 8 Feb 2025 18:10:37 -0800 Subject: [PATCH 230/311] Fixing AddScalarAndOperator unittest Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 40 +++++++++++-------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 4a31d41b29..5728bdd7c3 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -29,10 +29,14 @@ cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, } std::vector> cudm_op_conversion::get_identity_matrix() { - std::vector> identity_matrix( - dimensions_.size() * dimensions_.size(), {0.0, 0.0}); - for (size_t i = 0; i < dimensions_.size(); i++) { - identity_matrix[i * dimensions_.size() + i] = {1.0, 0.0}; + size_t dim = 1; + for (const auto &entry : dimensions_) { + dim *= entry.second; + } + + std::vector> identity_matrix(dim * dim, {0.0, 0.0}); + for (size_t i = 0; i < dim; i++) { + identity_matrix[i * dim + i] = {1.0, 0.0}; } return identity_matrix; @@ -40,13 +44,12 @@ std::vector> cudm_op_conversion::get_identity_matrix() { cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( const cudensitymatWrappedScalarCallback_t &scalar) { - cudensitymatOperatorTerm_t op_term; - std::vector space_mode_extents; for (const auto &dim : dimensions_) { space_mode_extents.push_back(dim.second); } + cudensitymatOperatorTerm_t op_term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); @@ -74,7 +77,7 @@ cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( return op_term; } -cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( +cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( const cudensitymatWrappedScalarCallback_t &scalar, const cudensitymatOperatorTerm_t &op) { if (!op) { @@ -86,24 +89,29 @@ cudensitymatOperatorTerm_t cudm_op_conversion::_callback_mult_op( space_mode_extents.push_back(dim.second); } - cudensitymatOperatorTerm_t scalar_term = _scalar_to_op(scalar); + cudensitymatOperatorTerm_t scalar_op = _scalar_to_op(scalar); - cudensitymatOperatorTerm_t new_opterm; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + if (!scalar_op) { + throw std::runtime_error("scalar_op is NULL."); + } + + cudensitymatOperator_t new_op; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle_, static_cast(dimensions_.size()), - space_mode_extents.data(), &new_opterm)); + space_mode_extents.data(), &new_op)); std::vector mode_action_duality(dimensions_.size(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, new_opterm, scalar_term, mode_action_duality.size(), {1.0, 0.0}, - scalar)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_op, scalar_op, + mode_action_duality.size(), + {1.0, 0.0}, scalar)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, new_opterm, op, mode_action_duality.size(), {1.0, 0.0}, scalar)); + handle_, new_op, op, mode_action_duality.size(), {1.0, 0.0}, + {nullptr, nullptr})); - return new_opterm; + return new_op; } std::variant Date: Sat, 8 Feb 2025 21:37:57 -0800 Subject: [PATCH 231/311] * Changing return type from double to std::complex * Adding function to get space_mode_extents * Adding more unittests for op_conversion Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_op_conversion.h | 18 ++-- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 87 +++++++++++++------ .../dynamics/test_cudm_op_conversion.cpp | 66 +++++++++++--- 3 files changed, 124 insertions(+), 47 deletions(-) diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index d69d7266c8..645542c78b 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -23,19 +23,23 @@ class cudm_op_conversion { // Tensor product of two operator terms std::variant + std::complex> tensor(const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2); // Multiplication of two operator terms std::variant + std::complex> mul(const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2); // Addition of two operator terms std::variant> get_identity_matrix(); + + std::vector get_space_mode_extents(); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 5728bdd7c3..022b4af1a3 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -42,13 +42,19 @@ std::vector> cudm_op_conversion::get_identity_matrix() { return identity_matrix; } -cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( - const cudensitymatWrappedScalarCallback_t &scalar) { +std::vector cudm_op_conversion::get_space_mode_extents() { std::vector space_mode_extents; for (const auto &dim : dimensions_) { space_mode_extents.push_back(dim.second); } + return space_mode_extents; +} + +cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( + const cudensitymatWrappedScalarCallback_t &scalar) { + std::vector space_mode_extents = get_space_mode_extents(); + cudensitymatOperatorTerm_t op_term; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); @@ -84,10 +90,7 @@ cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( throw std::invalid_argument("Invalid operator term (nullptr)."); } - std::vector space_mode_extents; - for (const auto &dim : dimensions_) { - space_mode_extents.push_back(dim.second); - } + std::vector space_mode_extents = get_space_mode_extents(); cudensitymatOperatorTerm_t scalar_op = _scalar_to_op(scalar); @@ -115,20 +118,49 @@ cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( } std::variant + std::complex> cudm_op_conversion::tensor( const std::variant &op1, + cudensitymatWrappedScalarCallback_t, + std::complex> &op1, const std::variant &op2) { - if (std::holds_alternative(op1) || - std::holds_alternative(op2)) { - return std::get(op1) * std::get(op2); + cudensitymatWrappedScalarCallback_t, + std::complex> &op2) { + if (std::holds_alternative>(op1) && + std::holds_alternative>(op2)) { + return std::get>(op1) * + std::get>(op2); } - cudensitymatOperatorTerm_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, dimensions_.size(), - nullptr, &result)); + if (std::holds_alternative>(op1)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op1))), + std::get(op2)); + } + + if (std::holds_alternative>(op2)) { + return _callback_mult_op( + _wrap_callback(scalar_operator(std::get>(op2))), + std::get(op1)); + } + + if (std::holds_alternative(op1)) { + return tensor( + _scalar_to_op(std::get(op1)), + std::get(op2)); + } + + if (std::holds_alternative(op2)) { + return tensor( + _scalar_to_op(std::get(op2)), + std::get(op1)); + } + + std::vector space_mode_extents = get_space_mode_extents(); + + cudensitymatOperator_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle_, dimensions_.size(), space_mode_extents.data(), &result)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle_, result, std::get(op1), 0, {1.0, 0.0}, @@ -141,12 +173,13 @@ cudm_op_conversion::tensor( } std::variant -cudm_op_conversion::mul( - const std::variant &op1, - const std::variant &op2) { + std::complex> +cudm_op_conversion::mul(const std::variant> &op1, + const std::variant> &op2) { return tensor(op1, op2); } @@ -179,10 +212,11 @@ cudm_op_conversion::add(const std::variant(dimensions_.size()), 1); + std::vector space_mode_extents = get_space_mode_extents(); - cudensitymatOperatorTerm_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle_, num_space_modes, - nullptr, &result)); + cudensitymatOperator_t result; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle_, num_space_modes, space_mode_extents.data(), &result)); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle_, result, std::get(op1), 0, {1.0, 0.0}, @@ -214,10 +248,7 @@ cudm_op_conversion::evaluate( if (std::holds_alternative(op)) { const matrix_operator &mat_op = std::get(op); - std::vector space_mode_extents; - for (const auto &dim : dimensions_) { - space_mode_extents.push_back(dim.second); - } + std::vector space_mode_extents = get_space_mode_extents(); cudensitymatOperatorTerm_t opterm; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index a59c8d41ac..bf8a0c7403 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -121,27 +121,67 @@ TEST_F(CuDmOpConversion, AddScalarAndOperator) { ASSERT_TRUE(std::holds_alternative(final_result)); } +TEST_F(CuDmOpConversion, AddMatrixOperators) { + matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); + matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + + auto op_result1 = converter->evaluate(mat_op1); + auto op_result2 = converter->evaluate(mat_op2); + + auto final_result = converter->add(op_result1, op_result2); + + ASSERT_TRUE(std::holds_alternative(final_result)); +} + +TEST_F(CuDmOpConversion, MultiplyMatrixOperators) { + matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); + matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + + auto op_result1 = converter->evaluate(mat_op1); + auto op_result2 = converter->evaluate(mat_op2); + + auto final_result = converter->mul(op_result1, op_result2); + + ASSERT_TRUE(std::holds_alternative(final_result)); +} + +TEST_F(CuDmOpConversion, TensorOfMatrixOperators) { + matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); + matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + + auto op_result1 = converter->evaluate(mat_op1); + auto op_result2 = converter->evaluate(mat_op2); + + auto final_result = converter->tensor(op_result1, op_result2); + + ASSERT_TRUE(std::holds_alternative(final_result)); +} + TEST_F(CuDmOpConversion, TensorProductOfScalars) { auto result = converter->tensor(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative(result)); - EXPECT_EQ(std::get(result), 6.0); + EXPECT_TRUE(std::holds_alternative>(result)); + std::complex final_result = std::get>(result); + EXPECT_EQ(final_result.real(), 6); + EXPECT_EQ(final_result.imag(), 0); } -// TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { -// cudensitymatOperatorTerm_t op_term; -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm(handle, -// dimensions.size(), space_mode_extents.data(), &op_term)); +TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { + cudensitymatOperatorTerm_t op_term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, dimensions.size(), space_mode_extents.data(), &op_term)); -// auto result = converter->tensor(2.0, op_term); -// EXPECT_TRUE(std::holds_alternative(result)); + auto result = converter->tensor(2.0, op_term); + EXPECT_TRUE(std::holds_alternative(result)); -// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); -// } + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); +} TEST_F(CuDmOpConversion, MultiplyOperators) { - auto result = converter->mul(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative(result)); - EXPECT_EQ(std::get(result), 6.0); + auto result = converter->mul(6.0, 3.0); + EXPECT_TRUE(std::holds_alternative>(result)); + std::complex final_result = std::get>(result); + EXPECT_EQ(final_result.real(), 18); + EXPECT_EQ(final_result.imag(), 0); } TEST_F(CuDmOpConversion, MoveSemantics) { From a67cec965ce8996c8c083472627c0e82199e408b Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sun, 9 Feb 2025 15:51:59 -0800 Subject: [PATCH 232/311] Fixing RK4 Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_state.h | 2 +- runtime/cudaq/dynamics/cudm_state.cpp | 9 ++++-- .../cudaq/dynamics/runge_kutta_integrator.cpp | 32 +++++++++---------- .../dynamics/test_runge_kutta_integrator.cpp | 4 +-- 4 files changed, 23 insertions(+), 24 deletions(-) diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 1c7082cdfe..8dff591bc7 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -99,7 +99,7 @@ class cudm_state { /// @return The new state after multiplying scalar with the current state. cudm_state &operator*=(const std::complex &scalar); - cudm_state operator*(double scalar) &&; + cudm_state operator*(double scalar) const; private: std::vector> rawData_; diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 3b837101d3..7b20c677a9 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -181,9 +181,12 @@ cudm_state &cudm_state::operator*=(const std::complex &scalar) { return *this; } -cudm_state cudm_state::operator*(double scalar) && { - this->operator*=(scalar); - return std::move(*this); +cudm_state cudm_state::operator*(double scalar) const { + cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + + result *= std::complex(scalar, 0.0); + + return result; } std::string cudm_state::dump() const { diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp index 04ae89e595..4217edb48b 100644 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp @@ -51,31 +51,29 @@ void runge_kutta_integrator::integrate(double target_time) { this->state += k2; } else if (this->substeps_ == 4) { - // FIXME: implement it // Runge-Kutta method (4th order) - // cudm_state k1 = this->stepper->compute(this->state, this->t, - // step_size); k1 *= (step_size / 2.0); + cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); + k1 *= step_size; - // this->state += k1; + this->state += k1 * 0.5; - // cudm_state k2 = this->stepper->compute( - // this->state, this->t + step_size / 2.0, step_size); - // k2 *= (step_size / 2.0); + cudm_state k2 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k2 *= step_size; - // this->state += k2; + this->state += k2 * 0.5; - // cudm_state k3 = this->stepper->compute( - // this->state, this->t + step_size / 2.0, step_size); - // k3 *= step_size; + cudm_state k3 = this->stepper->compute( + this->state, this->t + step_size / 2.0, step_size); + k3 *= step_size; - // this->state += k3; + this->state += k3; - // cudm_state k4 = - // this->stepper->compute(this->state, this->t + step_size, - // step_size); - // k4 *= step_size; + cudm_state k4 = + this->stepper->compute(this->state, this->t + step_size, step_size); + k4 *= step_size; - // this->state += (k1 + ((k2 + k3) * 2.0) + k4) * (1.0 / 6.0); + this->state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); } // Update time diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 4de2dd526d..95eacce432 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -166,9 +166,7 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { }; cudaq::matrix_operator::define(op_id, {-1}, func); - // FIXME: enable all orders - // for (int integratorOrder : {1, 2, 4}) { - for (int integratorOrder : {1, 2}) { + for (int integratorOrder : {1, 2, 4}) { std::cout << "Test RK order " << integratorOrder << "\n"; auto op = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, From c67d19ca574d531f7d021b2afe8810a9601e62f3 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Sun, 9 Feb 2025 16:18:53 -0800 Subject: [PATCH 233/311] Using cudensitymatStateComputeScaling for multiplication of state with a scalar Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_state.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 7b20c677a9..281ee5acce 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -182,9 +182,20 @@ cudm_state &cudm_state::operator*=(const std::complex &scalar) { } cudm_state cudm_state::operator*(double scalar) const { - cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + void *gpuScalar; + HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); + + std::complex complexScalar(scalar, 0.0); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &complexScalar, + sizeof(std::complex), + cudaMemcpyHostToDevice)); - result *= std::complex(scalar, 0.0); + cudm_state result(handle_, rawData_, hilbertSpaceDims_); + + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(handle_, result.state_, gpuScalar, 0)); + + HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); return result; } From 573952be0599c8b89fc87160393cacc952f0a85c Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 10 Feb 2025 00:08:38 +0000 Subject: [PATCH 234/311] Add expectation Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_expectation.h | 28 +++++++ runtime/cudaq/dynamics/CMakeLists.txt | 1 + runtime/cudaq/dynamics/cudm_expectation.cpp | 77 ++++++++++++++++++++ unittests/CMakeLists.txt | 1 + unittests/dynamics/test_cudm_expectation.cpp | 53 ++++++++++++++ 5 files changed, 160 insertions(+) create mode 100644 runtime/cudaq/cudm_expectation.h create mode 100644 runtime/cudaq/dynamics/cudm_expectation.cpp create mode 100644 unittests/dynamics/test_cudm_expectation.cpp diff --git a/runtime/cudaq/cudm_expectation.h b/runtime/cudaq/cudm_expectation.h new file mode 100644 index 0000000000..f345d70df1 --- /dev/null +++ b/runtime/cudaq/cudm_expectation.h @@ -0,0 +1,28 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include +namespace cudaq { + +class cudm_expectation { + cudensitymatHandle_t m_handle; + cudensitymatOperator_t m_hamOp; + cudensitymatExpectation_t m_expectation; + cudensitymatWorkspaceDescriptor_t m_workspace; + +public: + cudm_expectation(cudensitymatHandle_t handle, cudensitymatOperator_t op); + ~cudm_expectation(); + void prepare(cudensitymatState_t state); + std::complex compute(cudensitymatState_t state, double time); + +}; + +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 95d56934f1..7f2510fb96 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -27,6 +27,7 @@ set(CUDAQ_OPS_SRC helpers.cpp rydberg_hamiltonian.cpp cudm_op_conversion.cpp + cudm_expectation.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/cudm_expectation.cpp b/runtime/cudaq/dynamics/cudm_expectation.cpp new file mode 100644 index 0000000000..18f4b54b70 --- /dev/null +++ b/runtime/cudaq/dynamics/cudm_expectation.cpp @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/cudm_expectation.h" +#include "common/Logger.h" +#include "cudaq/cudm_error_handling.h" +#include "cudaq/cudm_helpers.h" +#include + +namespace cudaq { +cudm_expectation::cudm_expectation(cudensitymatHandle_t handle, + cudensitymatOperator_t op) + : m_handle(handle), m_hamOp(op) { + HANDLE_CUDM_ERROR( + cudensitymatCreateExpectation(m_handle, m_hamOp, &m_expectation)); + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(m_handle, &m_workspace)); +} + +cudm_expectation::~cudm_expectation() { + cudensitymatDestroyWorkspace(m_workspace); + cudensitymatDestroyExpectation(m_expectation); +} + +void cudm_expectation::prepare(cudensitymatState_t state) { + std::size_t freeMem = 0, totalMem = 0; + HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); + freeMem = static_cast(static_cast(freeMem) * 0.80); + + HANDLE_CUDM_ERROR(cudensitymatExpectationPrepare( + m_handle, m_expectation, state, CUDENSITYMAT_COMPUTE_64F, freeMem, + m_workspace, 0x0)); +} +std::complex cudm_expectation::compute(cudensitymatState_t state, + double time) { + // TODO: create a global scratch buffer + std::size_t requiredBufferSize = 0; + HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( + m_handle, m_workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); + + void *workspaceBuffer = nullptr; + if (requiredBufferSize > 0) { + cudaq::info("Required buffer size for expectation compute: {}", + requiredBufferSize); + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + workspaceBuffer = create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + m_handle, m_workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + } + + auto *expectationValue_d = + create_array_gpu(std::vector>(1, {0.0, 0.0})); + HANDLE_CUDM_ERROR(cudensitymatExpectationCompute( + m_handle, m_expectation, time, 0, nullptr, state, expectationValue_d, + m_workspace, 0x0)); + std::complex result; + HANDLE_CUDA_ERROR(cudaMemcpy(&result, expectationValue_d, + sizeof(std::complex), + cudaMemcpyDefault)); + destroy_array_gpu(expectationValue_d); + if (workspaceBuffer) { + destroy_array_gpu(workspaceBuffer); + } + return result; +} +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 99160e52f1..adbd82a980 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -296,6 +296,7 @@ if (CUDA_FOUND) dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp dynamics/test_cudm_op_conversion.cpp + dynamics/test_cudm_expectation.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp new file mode 100644 index 0000000000..b86e1cb37f --- /dev/null +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "test_mocks.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace cudaq; + +class CuDensityExpectationTest : public ::testing::Test { +protected: + cudensitymatHandle_t handle_; + + void SetUp() override { + // Create library handle + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + } + + void TearDown() override { + // Clean up + HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + } +}; + +TEST_F(CuDensityExpectationTest, checkCompute) { + const std::vector dims = {10}; + // Check number operator on boson Fock space + auto op = cudaq::matrix_operator::number(0); + auto cudmOp = cudaq::convert_to_cudensitymat_operator( + handle_, {}, op, dims); + + cudm_expectation expectation(handle_, cudmOp); + + for (std::size_t stateIdx = 0; stateIdx < dims[0]; ++stateIdx) { + std::vector> initialState(dims[0], 0.0); + initialState[stateIdx] = 1.0; + auto inputState = std::make_unique(handle_, initialState, dims); + expectation.prepare(inputState->get_impl()); + const auto expVal = expectation.compute(inputState->get_impl(), 0.0); + EXPECT_NEAR(expVal.real(), 1.0 * stateIdx, 1e-12); + EXPECT_NEAR(expVal.imag(), 0.0, 1e-12); + } +} From 7c64f066f6b28dd37739566ea401549c7ba04eab Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 10 Feb 2025 21:23:28 -0800 Subject: [PATCH 235/311] Moving wrapper callbacks into cudm_helpers.cpp and disabling cudm_op_conversion Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/CMakeLists.txt | 1 - runtime/cudaq/dynamics/cudm_helpers.cpp | 223 ++++-- runtime/cudaq/dynamics/cudm_op_conversion.cpp | 670 ++++++++---------- unittests/CMakeLists.txt | 1 - unittests/dynamics/test_cudm_helpers.cpp | 44 +- .../dynamics/test_cudm_op_conversion.cpp | 344 ++++----- 6 files changed, 653 insertions(+), 630 deletions(-) diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 7f2510fb96..b53c1237d5 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -26,7 +26,6 @@ set(CUDAQ_OPS_SRC manipulation.cpp helpers.cpp rydberg_hamiltonian.cpp - cudm_op_conversion.cpp cudm_expectation.cpp ) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index a900b99da1..7b5d40c502 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -12,6 +12,124 @@ using namespace cudaq; namespace cudaq { +cudensitymatWrappedScalarCallback_t +_wrap_callback(const scalar_operator &scalar_op) { + try { + std::complex evaluatedValue = scalar_op.evaluate({}); + + cudensitymatWrappedScalarCallback_t wrapped_callback; + wrapped_callback.callback = nullptr; + wrapped_callback.wrapper = new std::complex(evaluatedValue); + return wrapped_callback; + } catch (const std::exception &) { + } + + ScalarCallbackFunction generator = scalar_op.get_generator(); + + if (!generator) { + throw std::runtime_error( + "scalar_operator does not have a valid generator function."); + } + + auto callback = [](double time, int32_t num_params, const double params[], + cudaDataType_t data_type, + void *scalar_storage) -> int32_t { + try { + scalar_operator *scalar_op = + static_cast(scalar_storage); + + std::map> param_map; + for (size_t i = 0; i < num_params; i++) { + param_map[std::to_string(i)] = params[i]; + } + + std::complex result = scalar_op->evaluate(param_map); + + if (data_type == CUDA_C_64F) { + *reinterpret_cast(scalar_storage) = + make_cuDoubleComplex(result.real(), result.imag()); + } else if (data_type == CUDA_C_32F) { + *reinterpret_cast(scalar_storage) = + make_cuFloatComplex(static_cast(result.real()), + static_cast(result.imag())); + } else { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in scalar callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedScalarCallback_t wrappedCallback; + wrappedCallback.callback = callback; + wrappedCallback.wrapper = new scalar_operator(scalar_op); + + return wrappedCallback; +} + +cudensitymatWrappedTensorCallback_t +_wrap_callback_tensor(const matrix_operator &op) { + auto callback = + [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, + const int64_t mode_extents[], const int32_t diagonal_offsets[], + double time, int32_t num_params, const double params[], + cudaDataType_t data_type, void *tensor_storage) -> int32_t { + try { + matrix_operator *mat_op = static_cast(tensor_storage); + + std::map> param_map; + for (size_t i = 0; i < num_params; i++) { + param_map[std::to_string(i)] = params[i]; + } + + matrix_2 matrix_data = mat_op->to_matrix({}, param_map); + + std::size_t rows = matrix_data.get_rows(); + std::size_t cols = matrix_data.get_columns(); + + if (num_modes != rows) { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + if (data_type == CUDA_C_64F) { + cuDoubleComplex *storage = + static_cast(tensor_storage); + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + storage[i * cols + j] = make_cuDoubleComplex( + matrix_data[{i, j}].real(), matrix_data[{i, j}].imag()); + } + } + } else if (data_type == CUDA_C_32F) { + cuFloatComplex *storage = static_cast(tensor_storage); + for (size_t i = 0; i < rows; i++) { + for (size_t j = 0; j < cols; j++) { + storage[i * cols + j] = make_cuFloatComplex( + static_cast(matrix_data[{i, j}].real()), + static_cast(matrix_data[{i, j}].imag())); + } + } + } else { + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in tensor callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedTensorCallback_t wrapped_callback; + wrapped_callback.callback = callback; + wrapped_callback.wrapper = new matrix_operator(op); + + return wrapped_callback; +} + // Function to flatten a matrix into a 1D array (column major) std::vector> flatten_matrix(const matrix_2 &matrix) { std::vector> flat_matrix; @@ -42,6 +160,7 @@ get_subspace_extents(const std::vector &mode_extents, } // Function to create a cudensitymat elementary operator +// Need to use std::variant cudensitymatElementaryOperator_t create_elementary_operator( cudensitymatHandle_t handle, const std::vector &subspace_extents, const std::vector> &flat_matrix) { @@ -53,9 +172,6 @@ cudensitymatElementaryOperator_t create_elementary_operator( throw std::invalid_argument("subspace_extents cannot be empty."); } - std::cout << "Subspace extents size: " << subspace_extents.size() - << ", Matrix size: " << flat_matrix.size() << std::endl; - cudensitymatElementaryOperator_t cudm_elem_op = nullptr; // FIXME: leak (need to track this buffer somewhere and delete **after** the @@ -80,7 +196,8 @@ cudensitymatElementaryOperator_t create_elementary_operator( void append_elementary_operator_to_term( cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, const cudensitymatElementaryOperator_t &elem_op, - const std::vector °rees) { + const std::vector °rees, const std::vector &mode_extents, + const cudensitymatWrappedTensorCallback_t &wrapped_tensor_callback) { if (degrees.empty()) { throw std::invalid_argument("Degrees vector cannot be empty."); } @@ -89,10 +206,6 @@ void append_elementary_operator_to_term( throw std::invalid_argument("elem_op cannot be null."); } - std::cout << "Appending Elementary Operator to Term" - << " - Degrees: " << degrees.size() << " - Term: " << term - << std::endl; - for (int degree : degrees) { if (degree < 0) { throw std::out_of_range("Degree cannot be negative!"); @@ -100,26 +213,47 @@ void append_elementary_operator_to_term( } std::vector elem_ops = {elem_op}; + std::vector mode_action_duality(degrees.size(), 0); + + int32_t num_elementary_operators = 1; + int32_t num_operator_modes[] = {static_cast(degrees.size()) * 2}; + + const int64_t *operator_mode_extents[] = {mode_extents.data()}; + const int64_t *operator_mode_strides[] = {nullptr}; - std::cout << "elem_ops.data(): " << elem_ops.data() << std::endl; + std::vector state_modes_acted_on = degrees; + cudaDataType_t data_tye = CUDA_C_64F; + void *tensor_data[] = {elem_op}; + + cudensitymatWrappedTensorCallback_t tensor_callbacks[] = { + wrapped_tensor_callback}; + cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); - std::vector modeActionDuality(degrees.size(), 0); assert(elem_ops.size() == degrees.size()); - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, static_cast(degrees.size()), elem_ops.data(), - degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), - {nullptr, nullptr})); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendGeneralProduct( + handle, term, num_elementary_operators, num_operator_modes, + operator_mode_extents, operator_mode_strides, state_modes_acted_on.data(), + mode_action_duality.data(), data_tye, tensor_data, tensor_callbacks, + coefficient, {nullptr, nullptr})); } // Function to create and append a scalar to a term void append_scalar_to_term(cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, - const std::complex &coeff) { - // TODO: Implement handling for time-dependent scalars using - // cudensitymatScalarCallback_t - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 0, nullptr, nullptr, nullptr, - {make_cuDoubleComplex(coeff.real(), coeff.imag())}, {nullptr, nullptr})); + const scalar_operator &scalar_op) { + cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; + + if (!scalar_op.get_generator()) { + std::complex coeff = scalar_op.evaluate({}); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 0, nullptr, nullptr, nullptr, + {make_cuDoubleComplex(coeff.real(), coeff.imag())}, wrapped_callback)); + } else { + wrapped_callback = _wrap_callback(scalar_op); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, 0, nullptr, nullptr, nullptr, + {make_cuDoubleComplex(1.0, 0.0)}, wrapped_callback)); + } } void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, @@ -128,19 +262,8 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, throw std::invalid_argument("Invalid state provided to scale_state."); } - cudaEvent_t start, stop; - cudaEventCreate(&start); - cudaEventCreate(&stop); - cudaEventRecord(start, stream); - HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); - - cudaEventRecord(stop, stream); - cudaEventSynchronize(stop); - float milliseconds = 0; - cudaEventElapsedTime(&milliseconds, start, stop); - std::cout << "Time taken: " << milliseconds << " ms" << std::endl; } cudensitymatOperator_t @@ -181,7 +304,8 @@ compute_lindblad_operator(cudensitymatHandle_t handle, // Append the elementary operator to the term std::vector degrees = {0, 1}; - append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); + // FIXME + // append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; @@ -221,7 +345,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &operator_handle)); - std::vector elementary_operators; + // std::vector elementary_operators; for (const auto &product_op : op.get_terms()) { cudensitymatOperatorTerm_t term; @@ -235,21 +359,26 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( dynamic_cast(&component)) { auto subspace_extents = get_subspace_extents(mode_extents, elem_op->degrees); + cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = { + nullptr, nullptr}; + if (!parameters.empty()) { + wrapped_tensor_callback = _wrap_callback_tensor(*elem_op); + } + auto flat_matrix = flatten_matrix( elem_op->to_matrix(convert_dimensions(mode_extents), parameters)); auto cudm_elem_op = create_elementary_operator(handle, subspace_extents, flat_matrix); - elementary_operators.push_back(cudm_elem_op); + // elementary_operators.push_back(cudm_elem_op); append_elementary_operator_to_term(handle, term, cudm_elem_op, - elem_op->degrees); + elem_op->degrees, mode_extents, + wrapped_tensor_callback); } else if (const auto *scalar_op = dynamic_cast( &component)) { - // FIXME: do we need this code path? - // The product_op already has get_coefficient method. - auto coeff = scalar_op->evaluate(parameters); - append_scalar_to_term(handle, term, coeff); + // Need to confirm with Bettina + append_scalar_to_term(handle, term, *scalar_op); } else { // Catch anything that we don't know throw std::runtime_error("Unhandled type!"); @@ -257,12 +386,18 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( } // Handle the coefficient - auto coeff = product_op.get_coefficient().evaluate(parameters); - // Append the product operator term to the top-level operator + // Static value without parameter: as it is + // Static value with parameter: Callback + auto coeff = product_op.get_coefficient(); + cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; + + if (!coeff.get_generator()) { + wrapped_callback = _wrap_callback(coeff); + } + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, - make_cuDoubleComplex(coeff.real(), coeff.imag()), - {nullptr, nullptr})); + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + wrapped_callback)); // FIXME: leak // We must track these handles and destroy **after** evolve finishes diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/cudaq/dynamics/cudm_op_conversion.cpp index 022b4af1a3..2b45503cfa 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/cudaq/dynamics/cudm_op_conversion.cpp @@ -15,397 +15,283 @@ using namespace cudaq; namespace cudaq { -cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, - const std::map &dimensions, - std::shared_ptr schedule) - : handle_(handle), dimensions_(dimensions), schedule_(schedule) { - if (handle_ == nullptr) { - throw std::runtime_error("Handle cannot be null."); - } - - if (dimensions_.empty()) { - throw std::invalid_argument("Dimensions map must not be empty."); - } -} - -std::vector> cudm_op_conversion::get_identity_matrix() { - size_t dim = 1; - for (const auto &entry : dimensions_) { - dim *= entry.second; - } - - std::vector> identity_matrix(dim * dim, {0.0, 0.0}); - for (size_t i = 0; i < dim; i++) { - identity_matrix[i * dim + i] = {1.0, 0.0}; - } - - return identity_matrix; -} - -std::vector cudm_op_conversion::get_space_mode_extents() { - std::vector space_mode_extents; - for (const auto &dim : dimensions_) { - space_mode_extents.push_back(dim.second); - } - - return space_mode_extents; -} - -cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( - const cudensitymatWrappedScalarCallback_t &scalar) { - std::vector space_mode_extents = get_space_mode_extents(); - - cudensitymatOperatorTerm_t op_term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); - - void *tensor_data = create_array_gpu(get_identity_matrix()); - if (!tensor_data) { - throw std::runtime_error("Failed to allocate GPU memory for tensor_data."); - } - - std::vector mode_action_duality(dimensions_.size(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - - cudensitymatElementaryOperator_t identity; - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle_, dimensions_.size(), space_mode_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), - CUDA_C_64F, tensor_data, {nullptr, nullptr}, &identity)); - - std::vector states_modes_acted_on(dimensions_.size()); - std::iota(states_modes_acted_on.begin(), states_modes_acted_on.end(), 0); - - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle_, op_term, 1, &identity, states_modes_acted_on.data(), - mode_action_duality.data(), {1.0, 0.0}, scalar)); - - return op_term; -} - -cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( - const cudensitymatWrappedScalarCallback_t &scalar, - const cudensitymatOperatorTerm_t &op) { - if (!op) { - throw std::invalid_argument("Invalid operator term (nullptr)."); - } - - std::vector space_mode_extents = get_space_mode_extents(); - - cudensitymatOperatorTerm_t scalar_op = _scalar_to_op(scalar); - - if (!scalar_op) { - throw std::runtime_error("scalar_op is NULL."); - } - - cudensitymatOperator_t new_op; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle_, static_cast(dimensions_.size()), - space_mode_extents.data(), &new_op)); - - std::vector mode_action_duality(dimensions_.size(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_op, scalar_op, - mode_action_duality.size(), - {1.0, 0.0}, scalar)); - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, new_op, op, mode_action_duality.size(), {1.0, 0.0}, - {nullptr, nullptr})); - - return new_op; -} - -std::variant> -cudm_op_conversion::tensor( - const std::variant> &op1, - const std::variant> &op2) { - if (std::holds_alternative>(op1) && - std::holds_alternative>(op2)) { - return std::get>(op1) * - std::get>(op2); - } - - if (std::holds_alternative>(op1)) { - return _callback_mult_op( - _wrap_callback(scalar_operator(std::get>(op1))), - std::get(op2)); - } - - if (std::holds_alternative>(op2)) { - return _callback_mult_op( - _wrap_callback(scalar_operator(std::get>(op2))), - std::get(op1)); - } - - if (std::holds_alternative(op1)) { - return tensor( - _scalar_to_op(std::get(op1)), - std::get(op2)); - } - - if (std::holds_alternative(op2)) { - return tensor( - _scalar_to_op(std::get(op2)), - std::get(op1)); - } - - std::vector space_mode_extents = get_space_mode_extents(); - - cudensitymatOperator_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle_, dimensions_.size(), space_mode_extents.data(), &result)); - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, result, std::get(op1), 0, {1.0, 0.0}, - {nullptr, nullptr})); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, result, std::get(op2), 0, {1.0, 0.0}, - {nullptr, nullptr})); - - return result; -} - -std::variant> -cudm_op_conversion::mul(const std::variant> &op1, - const std::variant> &op2) { - return tensor(op1, op2); -} - -std::variant> -cudm_op_conversion::add(const std::variant> &op1, - const std::variant> &op2) { - if (std::holds_alternative>(op1) && - std::holds_alternative>(op2)) { - return std::get>(op1) + - std::get>(op2); - } - - if (std::holds_alternative>(op1)) { - return _callback_mult_op( - _wrap_callback(scalar_operator(std::get>(op1))), - std::get(op2)); - } - - if (std::holds_alternative>(op2)) { - return _callback_mult_op( - _wrap_callback(scalar_operator(std::get>(op2))), - std::get(op1)); - } - - // FIXME: Need to check later - int32_t num_space_modes = - std::max(static_cast(dimensions_.size()), 1); - std::vector space_mode_extents = get_space_mode_extents(); - - cudensitymatOperator_t result; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle_, num_space_modes, space_mode_extents.data(), &result)); - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, result, std::get(op1), 0, {1.0, 0.0}, - {nullptr, nullptr})); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle_, result, std::get(op2), 0, {1.0, 0.0}, - {nullptr, nullptr})); - - return result; -} - -std::variant> -cudm_op_conversion::evaluate( - const std::variant> &op) { - if (std::holds_alternative(op)) { - const scalar_operator &scalar_op = std::get(op); - - ScalarCallbackFunction generator = scalar_op.get_generator(); - - if (!generator) { - return scalar_op.evaluate({}); - } else { - return _wrap_callback(scalar_op); - } - } - - if (std::holds_alternative(op)) { - const matrix_operator &mat_op = std::get(op); - - std::vector space_mode_extents = get_space_mode_extents(); - - cudensitymatOperatorTerm_t opterm; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle_, dimensions_.size(), space_mode_extents.data(), &opterm)); - - cudensitymatElementaryOperator_t elem_op; - cudensitymatWrappedTensorCallback_t callback = - _wrap_callback_tensor(mat_op); - - auto flat_matrix = flatten_matrix(mat_op.to_matrix(dimensions_, {})); - - void *tensor_data = create_array_gpu(flat_matrix); - if (!tensor_data) { - throw std::runtime_error( - "Failed to allocate GPU memory for tensor_data."); - } - - std::vector mode_action_duality( - mat_op.degrees.size(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE); - - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle_, mat_op.degrees.size(), space_mode_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), - CUDA_C_64F, tensor_data, callback, &elem_op)); - - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle_, opterm, 1, &elem_op, mat_op.degrees.data(), - mode_action_duality.data(), {1.0, 0.0}, {nullptr, nullptr})); - - return opterm; - } - - if (std::holds_alternative>(op)) { - throw std::runtime_error( - "Handling of product_operator is not implemented."); - } - - throw std::runtime_error( - "Unknown operator type in cudm_op_conversion::evaluate."); -} - -cudensitymatWrappedScalarCallback_t -cudm_op_conversion::_wrap_callback(const scalar_operator &scalar_op) { - try { - std::complex evaluatedValue = scalar_op.evaluate({}); - - cudensitymatWrappedScalarCallback_t wrapped_callback; - wrapped_callback.callback = nullptr; - wrapped_callback.wrapper = new std::complex(evaluatedValue); - return wrapped_callback; - } catch (const std::exception &) { - } - - ScalarCallbackFunction generator = scalar_op.get_generator(); - - if (!generator) { - throw std::runtime_error( - "scalar_operator does not have a valid generator function."); - } - - auto callback = [](double time, int32_t num_params, const double params[], - cudaDataType_t data_type, - void *scalar_storage) -> int32_t { - try { - scalar_operator *scalar_op = - static_cast(scalar_storage); - - std::map> param_map; - for (size_t i = 0; i < num_params; i++) { - param_map[std::to_string(i)] = params[i]; - } - - std::complex result = scalar_op->evaluate(param_map); - - if (data_type == CUDA_C_64F) { - *reinterpret_cast(scalar_storage) = - make_cuDoubleComplex(result.real(), result.imag()); - } else if (data_type == CUDA_C_32F) { - *reinterpret_cast(scalar_storage) = - make_cuFloatComplex(static_cast(result.real()), - static_cast(result.imag())); - } else { - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - return CUDENSITYMAT_STATUS_SUCCESS; - } catch (const std::exception &e) { - std::cerr << "Error in scalar callback: " << e.what() << std::endl; - return CUDENSITYMAT_STATUS_INTERNAL_ERROR; - } - }; - - cudensitymatWrappedScalarCallback_t wrappedCallback; - wrappedCallback.callback = callback; - wrappedCallback.wrapper = new scalar_operator(scalar_op); - - return wrappedCallback; -} - -cudensitymatWrappedTensorCallback_t -cudm_op_conversion::_wrap_callback_tensor(const matrix_operator &op) { - auto callback = - [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, - const int64_t mode_extents[], const int32_t diagonal_offsets[], - double time, int32_t num_params, const double params[], - cudaDataType_t data_type, void *tensor_storage) -> int32_t { - try { - matrix_operator *mat_op = static_cast(tensor_storage); - - std::map> param_map; - for (size_t i = 0; i < num_params; i++) { - param_map[std::to_string(i)] = params[i]; - } - - matrix_2 matrix_data = mat_op->to_matrix({}, param_map); - - std::size_t rows = matrix_data.get_rows(); - std::size_t cols = matrix_data.get_columns(); - - if (num_modes != rows) { - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - if (data_type == CUDA_C_64F) { - cuDoubleComplex *storage = - static_cast(tensor_storage); - for (size_t i = 0; i < rows; i++) { - for (size_t j = 0; j < cols; j++) { - storage[i * cols + j] = make_cuDoubleComplex( - matrix_data[{i, j}].real(), matrix_data[{i, j}].imag()); - } - } - } else if (data_type == CUDA_C_32F) { - cuFloatComplex *storage = static_cast(tensor_storage); - for (size_t i = 0; i < rows; i++) { - for (size_t j = 0; j < cols; j++) { - storage[i * cols + j] = make_cuFloatComplex( - static_cast(matrix_data[{i, j}].real()), - static_cast(matrix_data[{i, j}].imag())); - } - } - } else { - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - return CUDENSITYMAT_STATUS_SUCCESS; - } catch (const std::exception &e) { - std::cerr << "Error in tensor callback: " << e.what() << std::endl; - return CUDENSITYMAT_STATUS_INTERNAL_ERROR; - } - }; - - cudensitymatWrappedTensorCallback_t wrapped_callback; - wrapped_callback.callback = callback; - wrapped_callback.wrapper = new matrix_operator(op); - - return wrapped_callback; -} +// cudm_op_conversion::cudm_op_conversion(const cudensitymatHandle_t handle, +// const std::map &dimensions, +// std::shared_ptr schedule) +// : handle_(handle), dimensions_(dimensions), schedule_(schedule) { +// if (handle_ == nullptr) { +// throw std::runtime_error("Handle cannot be null."); +// } + +// if (dimensions_.empty()) { +// throw std::invalid_argument("Dimensions map must not be empty."); +// } +// } + +// std::vector> cudm_op_conversion::get_identity_matrix() { +// size_t dim = 1; +// for (const auto &entry : dimensions_) { +// dim *= entry.second; +// } + +// std::vector> identity_matrix(dim * dim, {0.0, 0.0}); +// for (size_t i = 0; i < dim; i++) { +// identity_matrix[i * dim + i] = {1.0, 0.0}; +// } + +// return identity_matrix; +// } + +// std::vector cudm_op_conversion::get_space_mode_extents() { +// std::vector space_mode_extents; +// for (const auto &dim : dimensions_) { +// space_mode_extents.push_back(dim.second); +// } + +// return space_mode_extents; +// } + +// cudensitymatOperatorTerm_t cudm_op_conversion::_scalar_to_op( +// const cudensitymatWrappedScalarCallback_t &scalar) { +// std::vector space_mode_extents = get_space_mode_extents(); + +// cudensitymatOperatorTerm_t op_term; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( +// handle_, dimensions_.size(), space_mode_extents.data(), &op_term)); + +// void *tensor_data = create_array_gpu(get_identity_matrix()); +// if (!tensor_data) { +// throw std::runtime_error("Failed to allocate GPU memory for +// tensor_data."); +// } + +// std::vector mode_action_duality(dimensions_.size(), +// CUDENSITYMAT_OPERATOR_SPARSITY_NONE); + +// cudensitymatElementaryOperator_t identity; +// HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( +// handle_, dimensions_.size(), space_mode_extents.data(), +// CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), +// CUDA_C_64F, tensor_data, {nullptr, nullptr}, &identity)); + +// std::vector states_modes_acted_on(dimensions_.size()); +// std::iota(states_modes_acted_on.begin(), states_modes_acted_on.end(), 0); + +// HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( +// handle_, op_term, 1, &identity, states_modes_acted_on.data(), +// mode_action_duality.data(), {1.0, 0.0}, scalar)); + +// return op_term; +// } + +// cudensitymatOperator_t cudm_op_conversion::_callback_mult_op( +// const cudensitymatWrappedScalarCallback_t &scalar, +// const cudensitymatOperatorTerm_t &op) { +// if (!op) { +// throw std::invalid_argument("Invalid operator term (nullptr)."); +// } + +// std::vector space_mode_extents = get_space_mode_extents(); + +// cudensitymatOperatorTerm_t scalar_op = _scalar_to_op(scalar); + +// if (!scalar_op) { +// throw std::runtime_error("scalar_op is NULL."); +// } + +// cudensitymatOperator_t new_op; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( +// handle_, static_cast(dimensions_.size()), +// space_mode_extents.data(), &new_op)); + +// std::vector mode_action_duality(dimensions_.size(), +// CUDENSITYMAT_OPERATOR_SPARSITY_NONE); + +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle_, new_op, +// scalar_op, +// mode_action_duality.size(), +// {1.0, 0.0}, scalar)); + +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle_, new_op, op, mode_action_duality.size(), {1.0, 0.0}, +// {nullptr, nullptr})); + +// return new_op; +// } + +// std::variant> +// cudm_op_conversion::tensor( +// const std::variant> &op1, +// const std::variant> &op2) { +// if (std::holds_alternative>(op1) && +// std::holds_alternative>(op2)) { +// return std::get>(op1) * +// std::get>(op2); +// } + +// if (std::holds_alternative>(op1)) { +// return _callback_mult_op( +// _wrap_callback(scalar_operator(std::get>(op1))), +// std::get(op2)); +// } + +// if (std::holds_alternative>(op2)) { +// return _callback_mult_op( +// _wrap_callback(scalar_operator(std::get>(op2))), +// std::get(op1)); +// } + +// if (std::holds_alternative(op1)) { +// return tensor( +// _scalar_to_op(std::get(op1)), +// std::get(op2)); +// } + +// if (std::holds_alternative(op2)) { +// return tensor( +// _scalar_to_op(std::get(op2)), +// std::get(op1)); +// } + +// std::vector space_mode_extents = get_space_mode_extents(); + +// cudensitymatOperator_t result; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( +// handle_, dimensions_.size(), space_mode_extents.data(), &result)); + +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle_, result, std::get(op1), 0, {1.0, +// 0.0}, {nullptr, nullptr})); +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle_, result, std::get(op2), 0, {1.0, +// 0.0}, {nullptr, nullptr})); + +// return result; +// } + +// std::variant> +// cudm_op_conversion::mul(const std::variant> &op1, +// const std::variant> &op2) { +// return tensor(op1, op2); +// } + +// std::variant> +// cudm_op_conversion::add(const std::variant> &op1, +// const std::variant> &op2) { +// if (std::holds_alternative>(op1) && +// std::holds_alternative>(op2)) { +// return std::get>(op1) + +// std::get>(op2); +// } + +// if (std::holds_alternative>(op1)) { +// return _callback_mult_op( +// _wrap_callback(scalar_operator(std::get>(op1))), +// std::get(op2)); +// } + +// if (std::holds_alternative>(op2)) { +// return _callback_mult_op( +// _wrap_callback(scalar_operator(std::get>(op2))), +// std::get(op1)); +// } + +// // FIXME: Need to check later +// int32_t num_space_modes = +// std::max(static_cast(dimensions_.size()), 1); +// std::vector space_mode_extents = get_space_mode_extents(); + +// cudensitymatOperator_t result; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperator( +// handle_, num_space_modes, space_mode_extents.data(), &result)); + +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle_, result, std::get(op1), 0, {1.0, +// 0.0}, {nullptr, nullptr})); +// HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( +// handle_, result, std::get(op2), 0, {1.0, +// 0.0}, {nullptr, nullptr})); + +// return result; +// } + +// std::variant> +// cudm_op_conversion::evaluate( +// const std::variant> &op) { +// if (std::holds_alternative(op)) { +// const scalar_operator &scalar_op = std::get(op); + +// ScalarCallbackFunction generator = scalar_op.get_generator(); + +// if (!generator) { +// return scalar_op.evaluate({}); +// } else { +// return _wrap_callback(scalar_op); +// } +// } + +// if (std::holds_alternative(op)) { +// const matrix_operator &mat_op = std::get(op); + +// std::vector space_mode_extents = get_space_mode_extents(); + +// cudensitymatOperatorTerm_t opterm; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( +// handle_, dimensions_.size(), space_mode_extents.data(), &opterm)); + +// cudensitymatElementaryOperator_t elem_op; +// // Need to check if it is a static, use nullptr +// // or a callback and then only use callback +// cudensitymatWrappedTensorCallback_t callback = +// _wrap_callback_tensor(mat_op); + +// auto flat_matrix = flatten_matrix(mat_op.to_matrix(dimensions_, {})); + +// void *tensor_data = create_array_gpu(flat_matrix); +// if (!tensor_data) { +// throw std::runtime_error( +// "Failed to allocate GPU memory for tensor_data."); +// } + +// std::vector mode_action_duality( +// mat_op.degrees.size(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE); + +// HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( +// handle_, mat_op.degrees.size(), space_mode_extents.data(), +// CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, mode_action_duality.data(), +// CUDA_C_64F, tensor_data, callback, &elem_op)); + +// HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( +// handle_, opterm, 1, &elem_op, mat_op.degrees.data(), +// mode_action_duality.data(), {1.0, 0.0}, {nullptr, nullptr})); + +// return opterm; +// } + +// if (std::holds_alternative>(op)) { +// throw std::runtime_error( +// "Handling of product_operator is not implemented."); +// } + +// throw std::runtime_error( +// "Unknown operator type in cudm_op_conversion::evaluate."); +// } } // namespace cudaq \ No newline at end of file diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index adbd82a980..d76275e9c0 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -295,7 +295,6 @@ if (CUDA_FOUND) dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp - dynamics/test_cudm_op_conversion.cpp dynamics/test_cudm_expectation.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index aed09951a7..1c88d91e69 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include // Initialize operator_sum @@ -41,33 +42,34 @@ TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { } // Test for scale_state -TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { - std::vector mode_extents = {2}; +// TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { +// std::vector mode_extents = {2}; - std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; +// std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - cudaq::cudm_state state(handle, rawData, mode_extents); +// cudaq::cudm_state state(handle, rawData, mode_extents); - ASSERT_TRUE(state.is_initialized()); +// ASSERT_TRUE(state.is_initialized()); - EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), {2.0}, stream)); -} +// EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), {2.0}, +// stream)); +// } // Test for compute_lindblad_op -TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { - std::vector mode_extents = {2, 2}; - - cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); - cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); - std::vector c_ops = {c_op1, c_op2}; - - EXPECT_NO_THROW({ - auto lindblad_op = - cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); - ASSERT_NE(lindblad_op, nullptr); - cudensitymatDestroyOperator(lindblad_op); - }); -} +// TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { +// std::vector mode_extents = {2, 2}; + +// cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); +// cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); +// std::vector c_ops = {c_op1, c_op2}; + +// EXPECT_NO_THROW({ +// auto lindblad_op = +// cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); +// ASSERT_NE(lindblad_op, nullptr); +// cudensitymatDestroyOperator(lindblad_op); +// }); +// } // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { diff --git a/unittests/dynamics/test_cudm_op_conversion.cpp b/unittests/dynamics/test_cudm_op_conversion.cpp index bf8a0c7403..20e8228ac3 100644 --- a/unittests/dynamics/test_cudm_op_conversion.cpp +++ b/unittests/dynamics/test_cudm_op_conversion.cpp @@ -7,194 +7,196 @@ ******************************************************************************/ #include "cudaq/cudm_op_conversion.h" -#include "cudaq/operators.h" -#include "test_mocks.h" -#include -#include -#include -#include - -using namespace cudaq; - -class CuDmOpConversion : public ::testing::Test { -protected: - cudensitymatHandle_t handle; - std::map dimensions; - std::shared_ptr schedule; - std::unique_ptr converter; - std::vector space_mode_extents; - - void SetUp() override { - handle = mock_handle(); - dimensions = {{0, 2}, {1, 2}}; - for (const auto &dim : dimensions) { - space_mode_extents.push_back(dim.second); - } - schedule = std::shared_ptr(); - converter = - std::make_unique(handle, dimensions, schedule); - } -}; - -TEST_F(CuDmOpConversion, ConstructorValid) { - EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, schedule)); -} - -TEST_F(CuDmOpConversion, ConstructorEmptyDimensions) { - std::map empty_dimensions; - EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, schedule), - std::invalid_argument); -} - -TEST_F(CuDmOpConversion, ConstructorInvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; - EXPECT_THROW( - cudm_op_conversion converter(invalid_handle, dimensions, schedule), - std::runtime_error); -} - -TEST_F(CuDmOpConversion, EvaluateScalarConstant) { - scalar_operator scalar_op(2.5); - auto result = converter->evaluate(scalar_op); - - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), - std::complex(2.5, 0.0)); -} - -TEST_F(CuDmOpConversion, EvaluateScalarCallback) { - scalar_operator scalar_op([](std::map>) { - return std::complex(1.0, -1.0); - }); - auto result = converter->evaluate(scalar_op); - - ASSERT_TRUE( - std::holds_alternative(result)); -} - -TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { - matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); - auto result = converter->evaluate(mat_op); - - ASSERT_TRUE(std::holds_alternative(result)); -} - -TEST_F(CuDmOpConversion, EvaluateProductOperator) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(0); - product_operator product_op = op0 * op1; - EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); -} - -TEST_F(CuDmOpConversion, AddOperators) { - scalar_operator scalar_op1(2.0); - scalar_operator scalar_op2(3.0); - - auto result = converter->add(converter->evaluate(scalar_op1), - converter->evaluate(scalar_op2)); - - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), - std::complex(5.0, 0.0)); -} - -TEST_F(CuDmOpConversion, AddComplexScalars) { - std::complex scalar_1(2.0, 1.0); - std::complex scalar_2(3.0, -1.0); - - auto result = converter->add(scalar_1, scalar_2); - - ASSERT_TRUE(std::holds_alternative>(result)); - EXPECT_EQ(std::get>(result), - std::complex(5.0, 0.0)); -} - -TEST_F(CuDmOpConversion, AddScalarAndOperator) { - scalar_operator scalar_op(1.0); - matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); - - auto scalar_result = converter->evaluate(scalar_op); - auto op_result = converter->evaluate(mat_op); - - auto final_result = converter->add(scalar_result, op_result); - - ASSERT_TRUE(std::holds_alternative(final_result)); -} - -TEST_F(CuDmOpConversion, AddMatrixOperators) { - matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); - matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); - - auto op_result1 = converter->evaluate(mat_op1); - auto op_result2 = converter->evaluate(mat_op2); +// #include "cudaq/operators.h" +// #include "test_mocks.h" +// #include +// #include +// #include +// #include + +// using namespace cudaq; + +// class CuDmOpConversion : public ::testing::Test { +// protected: +// cudensitymatHandle_t handle; +// std::map dimensions; +// std::shared_ptr schedule; +// std::unique_ptr converter; +// std::vector space_mode_extents; + +// void SetUp() override { +// handle = mock_handle(); +// dimensions = {{0, 2}, {1, 2}}; +// for (const auto &dim : dimensions) { +// space_mode_extents.push_back(dim.second); +// } +// schedule = std::shared_ptr(); +// converter = +// std::make_unique(handle, dimensions, schedule); +// } +// }; + +// TEST_F(CuDmOpConversion, ConstructorValid) { +// EXPECT_NO_THROW(cudm_op_conversion converter(handle, dimensions, +// schedule)); +// } + +// TEST_F(CuDmOpConversion, ConstructorEmptyDimensions) { +// std::map empty_dimensions; +// EXPECT_THROW(cudm_op_conversion converter(handle, empty_dimensions, +// schedule), +// std::invalid_argument); +// } + +// TEST_F(CuDmOpConversion, ConstructorInvalidHandle) { +// cudensitymatHandle_t invalid_handle = nullptr; +// EXPECT_THROW( +// cudm_op_conversion converter(invalid_handle, dimensions, schedule), +// std::runtime_error); +// } + +// TEST_F(CuDmOpConversion, EvaluateScalarConstant) { +// scalar_operator scalar_op(2.5); +// auto result = converter->evaluate(scalar_op); + +// ASSERT_TRUE(std::holds_alternative>(result)); +// EXPECT_EQ(std::get>(result), +// std::complex(2.5, 0.0)); +// } + +// TEST_F(CuDmOpConversion, EvaluateScalarCallback) { +// scalar_operator scalar_op([](std::map>) { +// return std::complex(1.0, -1.0); +// }); +// auto result = converter->evaluate(scalar_op); + +// ASSERT_TRUE( +// std::holds_alternative(result)); +// } + +// TEST_F(CuDmOpConversion, EvaluateMatrixOperator) { +// matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); +// auto result = converter->evaluate(mat_op); + +// ASSERT_TRUE(std::holds_alternative(result)); +// } + +// TEST_F(CuDmOpConversion, EvaluateProductOperator) { +// auto op0 = cudaq::matrix_operator::annihilate(0); +// auto op1 = cudaq::matrix_operator::create(0); +// product_operator product_op = op0 * op1; +// EXPECT_THROW(converter->evaluate(product_op), std::runtime_error); +// } + +// TEST_F(CuDmOpConversion, AddOperators) { +// scalar_operator scalar_op1(2.0); +// scalar_operator scalar_op2(3.0); + +// auto result = converter->add(converter->evaluate(scalar_op1), +// converter->evaluate(scalar_op2)); + +// ASSERT_TRUE(std::holds_alternative>(result)); +// EXPECT_EQ(std::get>(result), +// std::complex(5.0, 0.0)); +// } + +// TEST_F(CuDmOpConversion, AddComplexScalars) { +// std::complex scalar_1(2.0, 1.0); +// std::complex scalar_2(3.0, -1.0); + +// auto result = converter->add(scalar_1, scalar_2); + +// ASSERT_TRUE(std::holds_alternative>(result)); +// EXPECT_EQ(std::get>(result), +// std::complex(5.0, 0.0)); +// } + +// TEST_F(CuDmOpConversion, AddScalarAndOperator) { +// scalar_operator scalar_op(1.0); +// matrix_operator mat_op = mock_matrix_operator("pauli_x", 0); + +// auto scalar_result = converter->evaluate(scalar_op); +// auto op_result = converter->evaluate(mat_op); + +// auto final_result = converter->add(scalar_result, op_result); + +// ASSERT_TRUE(std::holds_alternative(final_result)); +// } + +// TEST_F(CuDmOpConversion, AddMatrixOperators) { +// matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); +// matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); + +// auto op_result1 = converter->evaluate(mat_op1); +// auto op_result2 = converter->evaluate(mat_op2); - auto final_result = converter->add(op_result1, op_result2); +// auto final_result = converter->add(op_result1, op_result2); - ASSERT_TRUE(std::holds_alternative(final_result)); -} +// ASSERT_TRUE(std::holds_alternative(final_result)); +// } -TEST_F(CuDmOpConversion, MultiplyMatrixOperators) { - matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); - matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); +// TEST_F(CuDmOpConversion, MultiplyMatrixOperators) { +// matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); +// matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); - auto op_result1 = converter->evaluate(mat_op1); - auto op_result2 = converter->evaluate(mat_op2); +// auto op_result1 = converter->evaluate(mat_op1); +// auto op_result2 = converter->evaluate(mat_op2); - auto final_result = converter->mul(op_result1, op_result2); +// auto final_result = converter->mul(op_result1, op_result2); - ASSERT_TRUE(std::holds_alternative(final_result)); -} +// ASSERT_TRUE(std::holds_alternative(final_result)); +// } -TEST_F(CuDmOpConversion, TensorOfMatrixOperators) { - matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); - matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); +// TEST_F(CuDmOpConversion, TensorOfMatrixOperators) { +// matrix_operator mat_op1 = mock_matrix_operator("pauli_x", 0); +// matrix_operator mat_op2 = mock_matrix_operator("pauli_y", 0); - auto op_result1 = converter->evaluate(mat_op1); - auto op_result2 = converter->evaluate(mat_op2); +// auto op_result1 = converter->evaluate(mat_op1); +// auto op_result2 = converter->evaluate(mat_op2); - auto final_result = converter->tensor(op_result1, op_result2); +// auto final_result = converter->tensor(op_result1, op_result2); - ASSERT_TRUE(std::holds_alternative(final_result)); -} +// ASSERT_TRUE(std::holds_alternative(final_result)); +// } -TEST_F(CuDmOpConversion, TensorProductOfScalars) { - auto result = converter->tensor(2.0, 3.0); - EXPECT_TRUE(std::holds_alternative>(result)); - std::complex final_result = std::get>(result); - EXPECT_EQ(final_result.real(), 6); - EXPECT_EQ(final_result.imag(), 0); -} +// TEST_F(CuDmOpConversion, TensorProductOfScalars) { +// auto result = converter->tensor(2.0, 3.0); +// EXPECT_TRUE(std::holds_alternative>(result)); +// std::complex final_result = std::get>(result); +// EXPECT_EQ(final_result.real(), 6); +// EXPECT_EQ(final_result.imag(), 0); +// } -TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { - cudensitymatOperatorTerm_t op_term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, dimensions.size(), space_mode_extents.data(), &op_term)); +// TEST_F(CuDmOpConversion, TensorProductScalarAndOperator) { +// cudensitymatOperatorTerm_t op_term; +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( +// handle, dimensions.size(), space_mode_extents.data(), &op_term)); - auto result = converter->tensor(2.0, op_term); - EXPECT_TRUE(std::holds_alternative(result)); +// auto result = converter->tensor(2.0, op_term); +// EXPECT_TRUE(std::holds_alternative(result)); - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); -} +// HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(op_term)); +// } -TEST_F(CuDmOpConversion, MultiplyOperators) { - auto result = converter->mul(6.0, 3.0); - EXPECT_TRUE(std::holds_alternative>(result)); - std::complex final_result = std::get>(result); - EXPECT_EQ(final_result.real(), 18); - EXPECT_EQ(final_result.imag(), 0); -} +// TEST_F(CuDmOpConversion, MultiplyOperators) { +// auto result = converter->mul(6.0, 3.0); +// EXPECT_TRUE(std::holds_alternative>(result)); +// std::complex final_result = std::get>(result); +// EXPECT_EQ(final_result.real(), 18); +// EXPECT_EQ(final_result.imag(), 0); +// } -TEST_F(CuDmOpConversion, MoveSemantics) { - scalar_operator scalar_op(1.5); - auto scalar_result = converter->evaluate(scalar_op); +// TEST_F(CuDmOpConversion, MoveSemantics) { +// scalar_operator scalar_op(1.5); +// auto scalar_result = converter->evaluate(scalar_op); - EXPECT_TRUE(std::holds_alternative>(scalar_result)); +// EXPECT_TRUE(std::holds_alternative>(scalar_result)); - auto moved_result = std::move(scalar_result); +// auto moved_result = std::move(scalar_result); - EXPECT_TRUE(std::holds_alternative>(moved_result)); - EXPECT_EQ(std::get>(moved_result), - std::complex(1.5, 0.0)); +// EXPECT_TRUE(std::holds_alternative>(moved_result)); +// EXPECT_EQ(std::get>(moved_result), +// std::complex(1.5, 0.0)); - EXPECT_TRUE(scalar_result.index() != std::variant_npos); -} +// EXPECT_TRUE(scalar_result.index() != std::variant_npos); +// } From c503e7f8b2508e9dd9b5980ba41531bec06637e0 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Mon, 10 Feb 2025 21:57:53 -0800 Subject: [PATCH 236/311] Adding tests for callback functions Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 26 ++++++ runtime/cudaq/cudm_op_conversion.h | 6 +- unittests/dynamics/test_cudm_helpers.cpp | 106 ++++++++++++++++++++++- 3 files changed, 134 insertions(+), 4 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index ff56ea0541..17a1dcc681 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -39,6 +39,32 @@ cudensitymatOperator_t construct_liovillian( const std::vector &collapse_operators, double gamma); +cudensitymatWrappedScalarCallback_t +_wrap_callback(const scalar_operator &scalar_op); + +cudensitymatWrappedTensorCallback_t +_wrap_callback_tensor(const matrix_operator &op); + +void append_scalar_to_term(cudensitymatHandle_t handle, + cudensitymatOperatorTerm_t term, + const scalar_operator &scalar_op); + +std::map convert_dimensions(const std::vector &mode_extents); + +std::vector +get_subspace_extents(const std::vector &mode_extents, + const std::vector °rees); + +cudensitymatElementaryOperator_t create_elementary_operator( + cudensitymatHandle_t handle, const std::vector &subspace_extents, + const std::vector> &flat_matrix); + +void append_elementary_operator_to_term( + cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, + const cudensitymatElementaryOperator_t &elem_op, + const std::vector °rees, const std::vector &mode_extents, + const cudensitymatWrappedTensorCallback_t &wrapped_tensor_callback); + // Function for creating an array copy in GPU memory void *create_array_gpu(const std::vector> &cpu_array); diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/cudaq/cudm_op_conversion.h index 645542c78b..ed8db413b0 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/cudaq/cudm_op_conversion.h @@ -67,9 +67,9 @@ class cudm_op_conversion { const cudensitymatOperatorTerm_t &op); cudensitymatOperatorTerm_t _scalar_to_op(const cudensitymatWrappedScalarCallback_t &scalar); - cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &op); - cudensitymatWrappedTensorCallback_t - _wrap_callback_tensor(const matrix_operator &op); + // cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator + // &op); cudensitymatWrappedTensorCallback_t _wrap_callback_tensor(const + // matrix_operator &op); std::vector> get_identity_matrix(); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 1c88d91e69..1ef72ad901 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -6,6 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "test_mocks.h" #include #include #include @@ -73,7 +74,22 @@ TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { - std::vector mode_extents = {2, 2}; + std::vector mode_extents = mock_hilbert_space_dims(); + + auto op_sum = initialize_operator_sum(); + + EXPECT_NO_THROW({ + auto result = + cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); + cudensitymatDestroyOperator(result); + }); +} + +// Test with a higher-dimensional mode extent +TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { + std::vector mode_extents = {3, 3}; auto op_sum = initialize_operator_sum(); @@ -86,6 +102,94 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { }); } +// Test with a coefficient callback function +TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { + std::vector mode_extents = {2, 2}; + + auto callback_function = [](std::map>) { + return std::complex(1.5, 0.0); + }; + + cudaq::scalar_operator scalar_callback(callback_function); + + auto op_sum = scalar_callback * cudaq::matrix_operator::create(0); + + EXPECT_NO_THROW({ + auto result = + cudaq::convert_to_cudensitymat_operator( + handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); + cudensitymatDestroyOperator(result); + }); +} + +// Test with tensor callback function +TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { + std::vector mode_extents = {2, 2}; + + cudaq::matrix_operator matrix_op("CustomOp", {0, 1}); + + auto wrapped_tensor_callback = cudaq::_wrap_callback_tensor(matrix_op); + + ASSERT_NE(wrapped_tensor_callback.callback, nullptr); + + // auto op_sum = cudaq::operator_sum(matrix_op) + + // matrix_op; + + // EXPECT_NO_THROW({ + // auto result = + // cudaq::convert_to_cudensitymat_operator( + // handle, {}, op_sum, mode_extents); + // ASSERT_NE(result, nullptr); + // cudensitymatDestroyOperator(result); + // }); +} + +// Test for appending a scalar to a term +TEST_F(CuDensityMatHelpersTestFixture, AppendScalarToTerm) { + cudensitymatOperatorTerm_t term; + std::vector mode_extents = {2, 2}; + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term)); + + cudaq::scalar_operator scalar_op(2.0); + + EXPECT_NO_THROW(cudaq::append_scalar_to_term(handle, term, scalar_op)); + + cudensitymatDestroyOperatorTerm(term); +} + +// Test for appending a matrix_operator +TEST_F(CuDensityMatHelpersTestFixture, AppendElementaryOperatorToTerm) { + cudensitymatOperatorTerm_t term; + std::vector mode_extents = {2, 2}; + + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term)); + + cudaq::matrix_operator matrix_op = mock_matrix_operator("CustomOp", 0); + auto wrapped_tensor_callback = cudaq::_wrap_callback_tensor(matrix_op); + ASSERT_NE(wrapped_tensor_callback.callback, nullptr); + + auto flat_matrix = cudaq::flatten_matrix( + matrix_op.to_matrix(cudaq::convert_dimensions(mode_extents), {})); + auto subspace_extents = cudaq::get_subspace_extents(mode_extents, {0, 1}); + + auto elementary_op = + cudaq::create_elementary_operator(handle, subspace_extents, flat_matrix); + ASSERT_NE(elementary_op, nullptr); + + EXPECT_NO_THROW(cudaq::append_elementary_operator_to_term( + handle, term, elementary_op, {0, 1}, mode_extents, + wrapped_tensor_callback)); + + cudensitymatDestroyOperatorTerm(term); + cudensitymatDestroyElementaryOperator(elementary_op); +} + // Test invalid handle TEST_F(CuDensityMatHelpersTestFixture, InvalidHandle) { cudensitymatHandle_t invalid_handle = nullptr; From 0c8607b9ed99fc0b3b0eb507b78aab3c78ba598e Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Tue, 11 Feb 2025 08:16:21 +0000 Subject: [PATCH 237/311] [WIP] evolve single impl Signed-off-by: Thien Nguyen --- runtime/cudaq/dynamics/CMakeLists.txt | 1 + runtime/cudaq/dynamics/evolution.cpp | 159 ++++++++++++++-------- runtime/cudaq/dynamics/schedule.cpp | 71 +--------- runtime/cudaq/evolution.h | 111 ++++++++------- runtime/cudaq/schedule.h | 99 +++++--------- unittests/CMakeLists.txt | 2 + unittests/dynamics/test_evolve_single.cpp | 37 +++++ 7 files changed, 245 insertions(+), 235 deletions(-) create mode 100644 unittests/dynamics/test_evolve_single.cpp diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index b53c1237d5..5eefd2029c 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -27,6 +27,7 @@ set(CUDAQ_OPS_SRC helpers.cpp rydberg_hamiltonian.cpp cudm_expectation.cpp + evolution.cpp ) set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 75e5ca0875..1c68015385 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -7,6 +7,9 @@ ******************************************************************************/ #include "cudaq/evolution.h" +#include "cudaq/runge_kutta_integrator.h" +#include + #include #include #include @@ -15,64 +18,64 @@ namespace cudaq { // Can be removed -matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20) { - matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); - matrix_2 op_matrix_n = - matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); - - for (size_t i = 0; i < op_matrix.get_rows(); i++) { - result[{i, i}] = std::complex(1.0, 0.0); - op_matrix_n[{i, i}] = std::complex(1.0, 0.0); - } - - double factorial = 1.0; - for (int n = 1; n <= order; n++) { - op_matrix_n *= op_matrix; - factorial *= n; - result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; - } - - return result; -} +// matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20) { +// matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); +// matrix_2 op_matrix_n = +// matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); + +// for (size_t i = 0; i < op_matrix.get_rows(); i++) { +// result[{i, i}] = std::complex(1.0, 0.0); +// op_matrix_n[{i, i}] = std::complex(1.0, 0.0); +// } + +// double factorial = 1.0; +// for (int n = 1; n <= order; n++) { +// op_matrix_n *= op_matrix; +// factorial *= n; +// result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; +// } + +// return result; +// } -matrix_2 compute_step_matrix( - const operator_sum &hamiltonian, const std::map &dimensions, - const std::map> ¶meters, double dt, - bool use_gpu) { - matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); - op_matrix = dt * std::complex(0, -1) * op_matrix; - - if (use_gpu) { - // TODO: Implement GPU matrix exponential using CuPy or cuQuantum - throw std::runtime_error( - "GPU-based matrix exponentiation not implemented."); - } else { - return taylor_series_expm(op_matrix); - } -} +// matrix_2 compute_step_matrix( +// const operator_sum &hamiltonian, const std::map &dimensions, +// const std::map> ¶meters, double dt, +// bool use_gpu) { +// matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); +// op_matrix = dt * std::complex(0, -1) * op_matrix; + +// if (use_gpu) { +// // TODO: Implement GPU matrix exponential using CuPy or cuQuantum +// throw std::runtime_error( +// "GPU-based matrix exponentiation not implemented."); +// } else { +// return taylor_series_expm(op_matrix); +// } +// } -void add_noise_channel_for_step( - const std::string &step_kernel_name, cudaq::noise_model &noise_model, - const std::vector &collapse_operators, - const std::map &dimensions, - const std::map> ¶meters, double dt) { - for (const auto &collapse_op : collapse_operators) { - matrix_2 L = collapse_op.to_matrix(dimensions, parameters); - matrix_2 G = std::complex(-0.5, 0.0) * (L * L); - - // Kraus operators - matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); - matrix_2 M1 = std::sqrt(dt) * L; - - try { - noise_model.add_all_qubit_channel( - step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); - } catch (const std::exception &e) { - std::cerr << "Error adding noise channel: " << e.what() << std::endl; - throw; - } - } -} +// void add_noise_channel_for_step( +// const std::string &step_kernel_name, cudaq::noise_model &noise_model, +// const std::vector &collapse_operators, +// const std::map &dimensions, +// const std::map> ¶meters, double dt) { +// for (const auto &collapse_op : collapse_operators) { +// matrix_2 L = collapse_op.to_matrix(dimensions, parameters); +// matrix_2 G = std::complex(-0.5, 0.0) * (L * L); + +// // Kraus operators +// matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); +// matrix_2 M1 = std::sqrt(dt) * L; + +// try { +// noise_model.add_all_qubit_channel( +// step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); +// } catch (const std::exception &e) { +// std::cerr << "Error adding noise channel: " << e.what() << std::endl; +// throw; +// } +// } +// } // evolve_result launch_analog_hamiltonian_kernel(const std::string // &target_name, @@ -103,4 +106,48 @@ void add_noise_channel_for_step( // cudaq::ahs::AtomArrangement atoms; // } +evolve_result evolve_single( + const operator_sum &hamiltonian, + const std::map &dimensions, const Schedule &schedule, + state initial_state, + const std::vector *> + &collapse_operators, + const std::vector *> &observables, + bool store_intermediate_results, + std::shared_ptr> inputIntegrator, + std::optional shots_count) { + cudensitymatHandle_t handle; + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + std::vector dims; + for (const auto &[id, dim] : dimensions) + dims.emplace_back(dim); + auto cudmOp = cudaq::convert_to_cudensitymat_operator( + handle, {}, hamiltonian, dims); + auto time_stepper = std::make_shared(handle, cudmOp); + const std::vector> initialState = {{1.0, 0.0}, + {0.0, 0.0}}; + auto integrator = std::make_unique( + cudm_state(handle, initialState, dims), 0.0, time_stepper, 1); + integrator->set_option("dt", 0.001); + + std::vector expectations; + for (auto &obs : observables) + expectations.emplace_back(cudm_expectation( + handle, cudaq::convert_to_cudensitymat_operator( + handle, {}, *obs, dims))); + + for (const auto &step : schedule) { + std::cout << "Step: " << step << "\n"; + integrator->integrate(step); + auto [t, currentState] = integrator->get_state(); + for (auto &expectation : expectations) { + const auto expVal = expectation.compute(currentState.get_impl(), step); + std::cout << "Expectation value = " << expVal << "\n"; + } + } + + // TODO + return evolve_result(sample_result()); +} + } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/schedule.cpp b/runtime/cudaq/dynamics/schedule.cpp index 6e833acb4f..313d1322ca 100644 --- a/runtime/cudaq/dynamics/schedule.cpp +++ b/runtime/cudaq/dynamics/schedule.cpp @@ -13,69 +13,10 @@ namespace cudaq { // Constructor -Schedule::Schedule(std::vector> steps, - std::vector parameters, - std::function( - const std::string &, const std::complex &)> - value_function) - : _steps(steps), _parameters(parameters), _value_function(value_function), - _current_idx(-1) { - if (!_steps.empty()) { - m_ptr = &_steps[0]; - } else { - m_ptr = nullptr; - } -} - -// Dereference operator -Schedule::reference Schedule::operator*() const { return *m_ptr; } - -// Arrow operator -Schedule::pointer Schedule::operator->() { return m_ptr; } - -// Prefix increment -Schedule &Schedule::operator++() { - if (_current_idx + 1 < static_cast(_steps.size())) { - _current_idx++; - m_ptr = &_steps[_current_idx]; - } else { - throw std::out_of_range("No more steps in the schedule."); - } - return *this; -} - -// Postfix increment -Schedule Schedule::operator++(int) { - Schedule tmp = *this; - ++(*this); - return tmp; -} - -// Comparison operators -bool operator==(const Schedule &a, const Schedule &b) { - return a.m_ptr == b.m_ptr; -}; - -bool operator!=(const Schedule &a, const Schedule &b) { - return a.m_ptr != b.m_ptr; -}; - -// Reset schedule -void Schedule::reset() { - _current_idx = -1; - if (!_steps.empty()) { - m_ptr = &_steps[0]; - } else { - m_ptr = nullptr; - } -} - -// Get the current step -std::optional> Schedule::current_step() const { - if (_current_idx >= 0 && _current_idx < static_cast(_steps.size())) { - return _steps[_current_idx]; - } - return std::nullopt; -} - +Schedule::Schedule( + const std::vector &steps, + const std::vector ¶meters, + std::function(const std::string &, double)> + value_function) + : _steps(steps), _parameters(parameters), _value_function(value_function) {} } // namespace cudaq diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 85379f998a..309329e7b2 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -20,60 +20,71 @@ #include namespace cudaq { -class Evolution { -public: - /// Computes the Taylor series expansion of the matrix exponential. - static matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20); - /// Computes the evolution step matrix - static matrix_2 compute_step_matrix( - const operator_sum &hamiltonian, const std::map &dimensions, - const std::map> ¶meters, double dt, - bool use_gpu = false); +evolve_result evolve_single( + const operator_sum &hamiltonian, + const std::map &dimensions, const Schedule &schedule, + state initial_state, + const std::vector *> + &collapse_operators = {}, + const std::vector *> &observables = {}, + bool store_intermediate_results = false, + std::shared_ptr> integrator = nullptr, + std::optional shots_count = std::nullopt); +// class Evolution { +// public: +// /// Computes the Taylor series expansion of the matrix exponential. +// static matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20); - /// Adds noise channels based on collapse operators. - static void add_noise_channel_for_step( - const std::string &step_kernel_name, cudaq::noise_model &noise_model, - const std::vector &collapse_operators, - const std::map &dimensions, - const std::map> ¶meters, double dt); +// /// Computes the evolution step matrix +// static matrix_2 compute_step_matrix( +// const operator_sum &hamiltonian, const std::map &dimensions, +// const std::map> ¶meters, double dt, +// bool use_gpu = false); - /// Launches an analog Hamiltonian kernel for quantum simulations. - static evolve_result launch_analog_hamiltonian_kernel( - const std::string &target_name, const rydberg_hamiltonian &hamiltonian, - const Schedule &schedule, int shots_count, bool is_async = false); +// /// Adds noise channels based on collapse operators. +// static void add_noise_channel_for_step( +// const std::string &step_kernel_name, cudaq::noise_model &noise_model, +// const std::vector &collapse_operators, +// const std::map &dimensions, +// const std::map> ¶meters, double dt); - /// Generates evolution kernels for the simulation. - static std::vector evolution_kernel( - int num_qubits, - const std::function< - matrix_2(const std::map> &, double)> - &compute_step_matrix, - const std::vector tlist, - const std::vector>> - &schedule_parameters); +// /// Launches an analog Hamiltonian kernel for quantum simulations. +// static evolve_result launch_analog_hamiltonian_kernel( +// const std::string &target_name, const rydberg_hamiltonian &hamiltonian, +// const Schedule &schedule, int shots_count, bool is_async = false); - /// Evolves a single quantum state under a given `hamiltonian`. - static evolve_result - evolve_single(const operator_sum &hamiltonian, - const std::map &dimensions, - const std::shared_ptr &schedule, state initial_state, - const std::vector &collapse_operators = {}, - const std::vector &observables = {}, - bool store_intermediate_results = false, - std::shared_ptr> integrator = nullptr, - std::optional shots_count = std::nullopt); +// /// Generates evolution kernels for the simulation. +// static std::vector evolution_kernel( +// int num_qubits, +// const std::function< +// matrix_2(const std::map> &, double)> +// &compute_step_matrix, +// const std::vector tlist, +// const std::vector>> +// &schedule_parameters); - /// Evolves a single or multiple quantum states under a given `hamiltonian`. - /// Run only for dynamics target else throw error - static std::vector - evolve(const operator_sum &hamiltonian, const std::map &dimensions, - const std::shared_ptr &schedule, - const std::vector &initial_states, - const std::vector &collapse_operators = {}, - const std::vector &observables = {}, - bool store_intermediate_results = false, - std::shared_ptr> integrator = nullptr, - std::optional shots_count = std::nullopt); -}; +// /// Evolves a single quantum state under a given `hamiltonian`. +// static evolve_result +// evolve_single(const operator_sum &hamiltonian, +// const std::map &dimensions, +// const std::shared_ptr &schedule, state initial_state, +// const std::vector &collapse_operators = {}, +// const std::vector &observables = {}, +// bool store_intermediate_results = false, +// std::shared_ptr> integrator = nullptr, +// std::optional shots_count = std::nullopt); + +// /// Evolves a single or multiple quantum states under a given `hamiltonian`. +// /// Run only for dynamics target else throw error +// static std::vector +// evolve(const operator_sum &hamiltonian, const std::map &dimensions, +// const std::shared_ptr &schedule, +// const std::vector &initial_states, +// const std::vector &collapse_operators = {}, +// const std::vector &observables = {}, +// bool store_intermediate_results = false, +// std::shared_ptr> integrator = nullptr, +// std::optional shots_count = std::nullopt); +// }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h index 67d68fd951..4a7a62c06b 100644 --- a/runtime/cudaq/schedule.h +++ b/runtime/cudaq/schedule.h @@ -20,26 +20,43 @@ namespace cudaq { /// @brief Create a schedule for evaluating an operator expression at different /// steps. class Schedule { -public: - /// Iterator tags. May be superfluous. - using iterator_category = std::forward_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = std::complex; - using pointer = std::complex *; - using reference = std::complex &; - private: - pointer m_ptr; - std::vector> _steps; + std::vector _steps; std::vector _parameters; - std::function(const std::string &, - const std::complex &)> + std::function(const std::string &, double)> _value_function; - int _current_idx; public: - Schedule(pointer ptr) : m_ptr(ptr){}; + /// @brief Range-based iterator begin function + /// @return + std::vector::iterator begin() { return _steps.begin(); } + + /// @brief Range-based iterator end function + /// @return + std::vector::iterator end() { return _steps.end(); } + + /// @brief Range-based constant iterator begin function + /// @return + std::vector::const_iterator cbegin() const { return _steps.cbegin(); } + + /// @brief Range-based constant iterator end function + /// @return + std::vector::const_iterator cend() const { return _steps.cend(); } + /// @brief Range-based constant iterator begin function + /// @return + std::vector::const_iterator begin() const { return cbegin(); } + + /// @brief Range-based constant iterator end function + /// @return + std::vector::const_iterator end() const { return cend(); } + + const std::vector ¶meters() const { return _parameters; } + + std::function(const std::string &, double)> + value_function() const { + return _value_function; + } /// @brief Constructor. /// @arg steps: The sequence of steps in the schedule. Restricted to a vector /// of complex values. @@ -51,55 +68,9 @@ class Schedule { /// @details current_idx: Intializes the current index (_current_idx) to -1 to /// indicate that iteration has not yet begun. Once iteration starts, /// _current_idx will be used to track the position in the sequence of steps. - Schedule(const std::vector> steps, - const std::vector parameters, - std::function(const std::string &, - const std::complex &)> - value_function); - - /// Below, I define what I believe are the minimal necessary methods needed - /// for this to behave like an iterable. This should be revisited in the - /// implementation phase. - - // Pointers. - /// @brief `Dereference` operator to access the current step value. - /// @return Reference to current complex step value. - reference operator*() const; - - /// @brief Arrow operator to access the pointer the current step value. - /// @return Pointer to the current complex step value. - pointer operator->(); - - // Prefix increment. - /// @brief Prefix increment operator to move to the next step in the schedule. - /// @return Reference to the updated Schedule object. - Schedule &operator++(); - - // Postfix increment. - /// @brief `Postfix` increment operator to move to the next step in the - /// schedule. - /// @return Copy of the previous Schedule state. - Schedule operator++(int); - - // Comparison. - /// @brief Equality comparison operator. - /// @param a: First Schedule object. - /// @param b: Second Schedule object. - /// @return True if both schedules point to the same step, false otherwise - friend bool operator==(const Schedule &a, const Schedule &b); - - /// @brief Inequality comparison operator. - /// @param a: First Schedule object. - /// @param b: Second Schedule object. - /// @return True if both schedules point to different steps, false otherwise - friend bool operator!=(const Schedule &a, const Schedule &b); - - /// @brief Reset the schedule iterator to the beginning. - void reset(); - - /// @brief Get the current step in the schedule. - /// @return The current complex step value as an optional. If no valid step, - /// returns std::nullopt. - std::optional> current_step() const; + Schedule(const std::vector &steps, + const std::vector ¶meters = {}, + std::function(const std::string &, double)> + value_function = {}); }; } // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index d76275e9c0..1a7c87c3b6 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -296,6 +296,7 @@ if (CUDA_FOUND) dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp dynamics/test_cudm_expectation.cpp + dynamics/test_evolve_single.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) @@ -306,6 +307,7 @@ if (CUDA_FOUND) cudaq-spin cudaq-operators cudaq + nvqir-dynamics ${CUDENSITYMAT_ROOT}/lib/libcudensitymat.so.0 CUDA::cudart_static gtest_main diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp new file mode 100644 index 0000000000..b28392a0d0 --- /dev/null +++ b/unittests/dynamics/test_evolve_single.cpp @@ -0,0 +1,37 @@ +// /******************************************************************************* +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * +// ******************************************************************************/ + +#include "cudaq/evolution.h" +#include +#include +#include + +TEST(EvolveTester, checkSimple) { + const std::map dims = {{0, 2}}; + const std::string op_id = "pauli_x"; + auto func = [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = cudaq::matrix_2(2, 2); + mat[{1, 0}] = 1.0; + mat[{0, 1}] = 1.0; + return mat; + }; + cudaq::matrix_operator::define(op_id, {-1}, func); + auto ham = cudaq::product_operator( + std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, + cudaq::matrix_operator(op_id, {0})); + cudaq::Schedule schedule({0.0, 0.1, 0.2}); + auto initialState = + cudaq::state::from_data(std::vector>{1.0, 0.0}); + auto result = + cudaq::evolve_single(ham, dims, schedule, initialState, {}, {&ham}); +} From 16b69c12bbc90a4d4f37045422ca8cd981eae6f3 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 11 Feb 2025 10:41:04 -0800 Subject: [PATCH 238/311] * WIP fix for compute lindblad * Correcting tensor callback function name Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_expectation.h | 1 - runtime/cudaq/cudm_helpers.h | 2 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 84 +++++++++++-------- runtime/cudaq/dynamics/evolution.cpp | 3 +- runtime/cudaq/evolution.h | 28 ++++--- unittests/dynamics/test_cudm_helpers.cpp | 39 +++++---- .../dynamics/test_runge_kutta_integrator.cpp | 2 + 7 files changed, 91 insertions(+), 68 deletions(-) diff --git a/runtime/cudaq/cudm_expectation.h b/runtime/cudaq/cudm_expectation.h index f345d70df1..88e2a8ff50 100644 --- a/runtime/cudaq/cudm_expectation.h +++ b/runtime/cudaq/cudm_expectation.h @@ -22,7 +22,6 @@ class cudm_expectation { ~cudm_expectation(); void prepare(cudensitymatState_t state); std::complex compute(cudensitymatState_t state, double time); - }; } // namespace cudaq diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 17a1dcc681..1634481ae9 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -43,7 +43,7 @@ cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &scalar_op); cudensitymatWrappedTensorCallback_t -_wrap_callback_tensor(const matrix_operator &op); +_wrap_tensor_callback(const matrix_operator &op); void append_scalar_to_term(cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 7b5d40c502..c6cef1129c 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -71,7 +71,7 @@ _wrap_callback(const scalar_operator &scalar_op) { } cudensitymatWrappedTensorCallback_t -_wrap_callback_tensor(const matrix_operator &op) { +_wrap_tensor_callback(const matrix_operator &op) { auto callback = [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, const int64_t mode_extents[], const int32_t diagonal_offsets[], @@ -264,6 +264,8 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); + + HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); } cudensitymatOperator_t @@ -279,42 +281,55 @@ compute_lindblad_operator(cudensitymatHandle_t handle, handle, static_cast(mode_extents.size()), mode_extents.data(), &lindblad_op)); - for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument("Collapse operator must be a square matrix"); - } + try { + for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { + throw std::invalid_argument( + "Collapse operator must be a square matrix."); + } - auto flat_matrix = flatten_matrix(c_op); + auto flat_matrix = flatten_matrix(c_op); - // Create Operator term for LtL and add to lindblad_op - cudensitymatOperatorTerm_t term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &term)); + // Create Operator term for LtL and add to lindblad_op + cudensitymatOperatorTerm_t term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term)); - // Create elementary operator from c_op - cudensitymatElementaryOperator_t cudm_elem_op = - create_elementary_operator(handle, mode_extents, flat_matrix); + // Create elementary operator from c_op + cudensitymatElementaryOperator_t cudm_elem_op = + create_elementary_operator(handle, mode_extents, flat_matrix); - if (!cudm_elem_op) { - throw std::runtime_error( - "Failed to create elementary operator in compute_lindblad_operator."); - } + if (!cudm_elem_op) { + throw std::runtime_error("Failed to create elementary operator in " + "compute_lindblad_operator."); + } - // Append the elementary operator to the term - std::vector degrees = {0, 1}; - // FIXME - // append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); + // Append the elementary operator to the term + std::vector degrees = {0}; + cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = {nullptr, + nullptr}; + append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees, + mode_extents, wrapped_tensor_callback); - // Add term to lindblad operator - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm(handle, lindblad_op, term, - 0, {1.0}, scalarCallback)); + // Add term to lindblad operator + cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, lindblad_op, term, 0, make_cuDoubleComplex(1.0, 0.0), + scalarCallback)); - // Destroy intermediate resources - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(cudm_elem_op)); + // Destroy intermediate resources + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(cudm_elem_op)); + } + + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + } catch (const std::exception &e) { + std::cerr << "Exception in compute_lindblad_operator: " << e.what() + << std::endl; + cudensitymatDestroyOperator(lindblad_op); + return nullptr; } return lindblad_op; @@ -355,6 +370,8 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( mode_extents.data(), &term)); for (const auto &component : product_op.get_terms()) { + // No need to check type + // just call to_matrix on it if (const auto *elem_op = dynamic_cast(&component)) { auto subspace_extents = @@ -362,7 +379,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = { nullptr, nullptr}; if (!parameters.empty()) { - wrapped_tensor_callback = _wrap_callback_tensor(*elem_op); + wrapped_tensor_callback = _wrap_tensor_callback(*elem_op); } auto flat_matrix = flatten_matrix( @@ -374,11 +391,6 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( append_elementary_operator_to_term(handle, term, cudm_elem_op, elem_op->degrees, mode_extents, wrapped_tensor_callback); - } else if (const auto *scalar_op = - dynamic_cast( - &component)) { - // Need to confirm with Bettina - append_scalar_to_term(handle, term, *scalar_op); } else { // Catch anything that we don't know throw std::runtime_error("Unhandled type!"); diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 1c68015385..989a38c2c3 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -58,7 +58,8 @@ namespace cudaq { // const std::string &step_kernel_name, cudaq::noise_model &noise_model, // const std::vector &collapse_operators, // const std::map &dimensions, -// const std::map> ¶meters, double dt) { +// const std::map> ¶meters, double dt) +// { // for (const auto &collapse_op : collapse_operators) { // matrix_2 L = collapse_op.to_matrix(dimensions, parameters); // matrix_2 G = std::complex(-0.5, 0.0) * (L * L); diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 309329e7b2..27100bc494 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -34,20 +34,22 @@ evolve_result evolve_single( // class Evolution { // public: // /// Computes the Taylor series expansion of the matrix exponential. -// static matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20); +// static matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = +// 20); // /// Computes the evolution step matrix // static matrix_2 compute_step_matrix( // const operator_sum &hamiltonian, const std::map &dimensions, -// const std::map> ¶meters, double dt, -// bool use_gpu = false); +// const std::map> ¶meters, double +// dt, bool use_gpu = false); // /// Adds noise channels based on collapse operators. // static void add_noise_channel_for_step( // const std::string &step_kernel_name, cudaq::noise_model &noise_model, // const std::vector &collapse_operators, // const std::map &dimensions, -// const std::map> ¶meters, double dt); +// const std::map> ¶meters, double +// dt); // /// Launches an analog Hamiltonian kernel for quantum simulations. // static evolve_result launch_analog_hamiltonian_kernel( @@ -58,8 +60,8 @@ evolve_result evolve_single( // static std::vector evolution_kernel( // int num_qubits, // const std::function< -// matrix_2(const std::map> &, double)> -// &compute_step_matrix, +// matrix_2(const std::map> &, +// double)> &compute_step_matrix, // const std::vector tlist, // const std::vector>> // &schedule_parameters); @@ -68,17 +70,19 @@ evolve_result evolve_single( // static evolve_result // evolve_single(const operator_sum &hamiltonian, // const std::map &dimensions, -// const std::shared_ptr &schedule, state initial_state, -// const std::vector &collapse_operators = {}, -// const std::vector &observables = {}, -// bool store_intermediate_results = false, +// const std::shared_ptr &schedule, state +// initial_state, const std::vector +// &collapse_operators = {}, const std::vector +// &observables = {}, bool store_intermediate_results = false, // std::shared_ptr> integrator = nullptr, // std::optional shots_count = std::nullopt); -// /// Evolves a single or multiple quantum states under a given `hamiltonian`. +// /// Evolves a single or multiple quantum states under a given +// `hamiltonian`. // /// Run only for dynamics target else throw error // static std::vector -// evolve(const operator_sum &hamiltonian, const std::map &dimensions, +// evolve(const operator_sum &hamiltonian, const std::map +// &dimensions, // const std::shared_ptr &schedule, // const std::vector &initial_states, // const std::vector &collapse_operators = {}, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 1ef72ad901..641c3e203b 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -52,25 +52,29 @@ TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { // ASSERT_TRUE(state.is_initialized()); -// EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), {2.0}, +// EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), 2.0, // stream)); // } // Test for compute_lindblad_op -// TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { -// std::vector mode_extents = {2, 2}; - -// cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); -// cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); -// std::vector c_ops = {c_op1, c_op2}; - -// EXPECT_NO_THROW({ -// auto lindblad_op = -// cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); -// ASSERT_NE(lindblad_op, nullptr); -// cudensitymatDestroyOperator(lindblad_op); -// }); -// } +TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { + std::vector mode_extents = {2, 2}; + + cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); + cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); + std::vector c_ops = {c_op1, c_op2}; + + EXPECT_NO_THROW({ + auto lindblad_op = + cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + ASSERT_NE(lindblad_op, nullptr) + << "Error: Lindblad operator creation failed!"; + + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + + cudensitymatDestroyOperator(lindblad_op); + }); +} // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { @@ -82,6 +86,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { auto result = cudaq::convert_to_cudensitymat_operator( handle, {}, op_sum, mode_extents); + ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -129,7 +134,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { cudaq::matrix_operator matrix_op("CustomOp", {0, 1}); - auto wrapped_tensor_callback = cudaq::_wrap_callback_tensor(matrix_op); + auto wrapped_tensor_callback = cudaq::_wrap_tensor_callback(matrix_op); ASSERT_NE(wrapped_tensor_callback.callback, nullptr); @@ -171,7 +176,7 @@ TEST_F(CuDensityMatHelpersTestFixture, AppendElementaryOperatorToTerm) { &term)); cudaq::matrix_operator matrix_op = mock_matrix_operator("CustomOp", 0); - auto wrapped_tensor_callback = cudaq::_wrap_callback_tensor(matrix_op); + auto wrapped_tensor_callback = cudaq::_wrap_tensor_callback(matrix_op); ASSERT_NE(wrapped_tensor_callback.callback, nullptr); auto flat_matrix = cudaq::flatten_matrix( diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 95eacce432..1e5de6165c 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -205,4 +205,6 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); } + + // Add test to test tensor_callback } From f3bbf4b9871045366491c0dc2ea23cef30739174 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Tue, 11 Feb 2025 22:08:51 +0000 Subject: [PATCH 239/311] Fix issue with append_elementary_operator_to_term Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_helpers.h | 3 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 51 ++++++++------------ runtime/cudaq/dynamics/cudm_time_stepper.cpp | 4 +- unittests/dynamics/test_cudm_helpers.cpp | 3 +- 4 files changed, 23 insertions(+), 38 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 1634481ae9..86aee78d8d 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -62,8 +62,7 @@ cudensitymatElementaryOperator_t create_elementary_operator( void append_elementary_operator_to_term( cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, const cudensitymatElementaryOperator_t &elem_op, - const std::vector °rees, const std::vector &mode_extents, - const cudensitymatWrappedTensorCallback_t &wrapped_tensor_callback); + const std::vector °rees); // Function for creating an array copy in GPU memory void *create_array_gpu(const std::vector> &cpu_array); diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index c6cef1129c..aa579ee1de 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -196,8 +196,8 @@ cudensitymatElementaryOperator_t create_elementary_operator( void append_elementary_operator_to_term( cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, const cudensitymatElementaryOperator_t &elem_op, - const std::vector °rees, const std::vector &mode_extents, - const cudensitymatWrappedTensorCallback_t &wrapped_tensor_callback) { + const std::vector °rees) { + if (degrees.empty()) { throw std::invalid_argument("Degrees vector cannot be empty."); } @@ -213,28 +213,13 @@ void append_elementary_operator_to_term( } std::vector elem_ops = {elem_op}; - std::vector mode_action_duality(degrees.size(), 0); - - int32_t num_elementary_operators = 1; - int32_t num_operator_modes[] = {static_cast(degrees.size()) * 2}; - - const int64_t *operator_mode_extents[] = {mode_extents.data()}; - const int64_t *operator_mode_strides[] = {nullptr}; - - std::vector state_modes_acted_on = degrees; - cudaDataType_t data_tye = CUDA_C_64F; - void *tensor_data[] = {elem_op}; - - cudensitymatWrappedTensorCallback_t tensor_callbacks[] = { - wrapped_tensor_callback}; - cuDoubleComplex coefficient = make_cuDoubleComplex(1.0, 0.0); + std::vector modeActionDuality(degrees.size(), 0); assert(elem_ops.size() == degrees.size()); - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendGeneralProduct( - handle, term, num_elementary_operators, num_operator_modes, - operator_mode_extents, operator_mode_strides, state_modes_acted_on.data(), - mode_action_duality.data(), data_tye, tensor_data, tensor_callbacks, - coefficient, {nullptr, nullptr})); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, term, static_cast(degrees.size()), elem_ops.data(), + degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), + {nullptr, nullptr})); } // Function to create and append a scalar to a term @@ -308,10 +293,7 @@ compute_lindblad_operator(cudensitymatHandle_t handle, // Append the elementary operator to the term std::vector degrees = {0}; - cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = {nullptr, - nullptr}; - append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees, - mode_extents, wrapped_tensor_callback); + append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; @@ -379,6 +361,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = { nullptr, nullptr}; if (!parameters.empty()) { + std::cout << "_wrap_tensor_callback\n"; wrapped_tensor_callback = _wrap_tensor_callback(*elem_op); } @@ -389,8 +372,7 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( // elementary_operators.push_back(cudm_elem_op); append_elementary_operator_to_term(handle, term, cudm_elem_op, - elem_op->degrees, mode_extents, - wrapped_tensor_callback); + elem_op->degrees); } else { // Catch anything that we don't know throw std::runtime_error("Unhandled type!"); @@ -404,13 +386,18 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (!coeff.get_generator()) { + const auto coeffVal = coeff.evaluate(); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, + make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), + wrapped_callback)); + } else { wrapped_callback = _wrap_callback(coeff); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + wrapped_callback)); } - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), - wrapped_callback)); - // FIXME: leak // We must track these handles and destroy **after** evolve finishes // Destroy the term diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 271689d639..a8eb0bce0b 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -88,8 +88,8 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, // Apply the operator action HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( - handle_, liouvillian_, t, 1, std::vector({step_size}).data(), - state.get_impl(), next_state.get_impl(), workspace, 0x0)); + handle_, liouvillian_, t, 0, nullptr, state.get_impl(), + next_state.get_impl(), workspace, 0x0)); HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); // Cleanup diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 641c3e203b..0cb213a764 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -188,8 +188,7 @@ TEST_F(CuDensityMatHelpersTestFixture, AppendElementaryOperatorToTerm) { ASSERT_NE(elementary_op, nullptr); EXPECT_NO_THROW(cudaq::append_elementary_operator_to_term( - handle, term, elementary_op, {0, 1}, mode_extents, - wrapped_tensor_callback)); + handle, term, elementary_op, {0, 1})); cudensitymatDestroyOperatorTerm(term); cudensitymatDestroyElementaryOperator(elementary_op); From 4839077b34c8d50e5b5f095249576f5bf7a7fb8e Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 11 Feb 2025 14:52:38 -0800 Subject: [PATCH 240/311] * Moving the tensor callbck wrapper in create_elementary_operator * Moved other reponsibilities like flattening the matrix, gettting subspace extents to the create elementary operator function * Unittests updated * Wrapped cudm_helper in a class Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 119 ++++++++++-------- runtime/cudaq/dynamics/cudm_expectation.cpp | 10 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 106 ++++++++-------- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 4 +- runtime/cudaq/dynamics/evolution.cpp | 14 ++- unittests/dynamics/test_cudm_expectation.cpp | 5 +- unittests/dynamics/test_cudm_helpers.cpp | 76 +++++------ unittests/dynamics/test_cudm_time_stepper.cpp | 5 +- .../dynamics/test_runge_kutta_integrator.cpp | 5 +- 9 files changed, 178 insertions(+), 166 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 86aee78d8d..3bf2909431 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -17,61 +17,72 @@ #include namespace cudaq { -std::vector> flatten_matrix(const matrix_2 &matrix); - -void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, - double scale_factor, cudaStream_t stream); - -cudensitymatOperator_t -compute_lindblad_operator(cudensitymatHandle_t handle, - const std::vector &c_ops, - const std::vector &mode_extents); - -template -cudensitymatOperator_t convert_to_cudensitymat_operator( - cudensitymatHandle_t handle, - const std::map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents); - -cudensitymatOperator_t construct_liovillian( - cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, - const std::vector &collapse_operators, - double gamma); - -cudensitymatWrappedScalarCallback_t -_wrap_callback(const scalar_operator &scalar_op); - -cudensitymatWrappedTensorCallback_t -_wrap_tensor_callback(const matrix_operator &op); - -void append_scalar_to_term(cudensitymatHandle_t handle, - cudensitymatOperatorTerm_t term, - const scalar_operator &scalar_op); - -std::map convert_dimensions(const std::vector &mode_extents); - -std::vector -get_subspace_extents(const std::vector &mode_extents, - const std::vector °rees); - -cudensitymatElementaryOperator_t create_elementary_operator( - cudensitymatHandle_t handle, const std::vector &subspace_extents, - const std::vector> &flat_matrix); - -void append_elementary_operator_to_term( - cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, - const cudensitymatElementaryOperator_t &elem_op, - const std::vector °rees); - -// Function for creating an array copy in GPU memory -void *create_array_gpu(const std::vector> &cpu_array); - -// Function to detsroy a previously created array copy in GPU memory -void destroy_array_gpu(void *gpu_array); +class cudm_helper { +public: + explicit cudm_helper(cudensitymatHandle_t handle); + ~cudm_helper(); + + // Matrix flattening + std::vector> flatten_matrix(const matrix_2 &matrix); + + // State Operations + void scale_state(cudensitymatState_t state, double scale_factor, + cudaStream_t stream); + + // Compute Lindblad Operator + cudensitymatOperator_t + compute_lindblad_operator(const std::vector &c_ops, + const std::vector &mode_extents); + + // Convert operator sum to cudensitymat operator + template + cudensitymatOperator_t convert_to_cudensitymat_operator( + const std::map> ¶meters, + const operator_sum &op, + const std::vector &mode_extents); + + // Construct Liouvillian + cudensitymatOperator_t construct_liouvillian( + const cudensitymatOperator_t &hamiltonian, + const std::vector &collapse_operators, + double gamma); + + // Helper Functions + std::map + convert_dimensions(const std::vector &mode_extents); + std::vector + get_subspace_extents(const std::vector &mode_extents, + const std::vector °rees); + + // Callback Wrappers + cudensitymatWrappedScalarCallback_t + _wrap_callback(const scalar_operator &scalar_op); + cudensitymatWrappedTensorCallback_t + _wrap_tensor_callback(const matrix_operator &op); + + // Elementary Operator Functions + void append_scalar_to_term(cudensitymatOperatorTerm_t term, + const scalar_operator &scalar_op); + cudensitymatElementaryOperator_t create_elementary_operator( + const cudaq::matrix_operator *elem_op, + const std::map> ¶meters, + const std::vector &mode_extents); + void append_elementary_operator_to_term( + cudensitymatOperatorTerm_t term, + const cudensitymatElementaryOperator_t &elem_op, + const std::vector °rees); + + // GPU memory management + static void * + create_array_gpu(const std::vector> &cpu_array); + static void destroy_array_gpu(void *gpu_array); + +private: + cudensitymatHandle_t handle; +}; extern template cudensitymatOperator_t -convert_to_cudensitymat_operator( - cudensitymatHandle_t, const std::map> &, +cudm_helper::convert_to_cudensitymat_operator( + const std::map> &, const operator_sum &, const std::vector &); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_expectation.cpp b/runtime/cudaq/dynamics/cudm_expectation.cpp index 18f4b54b70..441666f2df 100644 --- a/runtime/cudaq/dynamics/cudm_expectation.cpp +++ b/runtime/cudaq/dynamics/cudm_expectation.cpp @@ -50,7 +50,7 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, // Allocate GPU storage for workspace buffer const std::size_t bufferVolume = requiredBufferSize / sizeof(std::complex); - workspaceBuffer = create_array_gpu( + workspaceBuffer = cudm_helper::create_array_gpu( std::vector>(bufferVolume, {0.0, 0.0})); // Attach workspace buffer @@ -59,8 +59,8 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); } - auto *expectationValue_d = - create_array_gpu(std::vector>(1, {0.0, 0.0})); + auto *expectationValue_d = cudm_helper::create_array_gpu( + std::vector>(1, {0.0, 0.0})); HANDLE_CUDM_ERROR(cudensitymatExpectationCompute( m_handle, m_expectation, time, 0, nullptr, state, expectationValue_d, m_workspace, 0x0)); @@ -68,9 +68,9 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, HANDLE_CUDA_ERROR(cudaMemcpy(&result, expectationValue_d, sizeof(std::complex), cudaMemcpyDefault)); - destroy_array_gpu(expectationValue_d); + cudm_helper::destroy_array_gpu(expectationValue_d); if (workspaceBuffer) { - destroy_array_gpu(workspaceBuffer); + cudm_helper::destroy_array_gpu(workspaceBuffer); } return result; } diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index aa579ee1de..50fc655e8e 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -12,8 +12,16 @@ using namespace cudaq; namespace cudaq { +cudm_helper::cudm_helper(cudensitymatHandle_t handle) : handle(handle) {} + +cudm_helper::~cudm_helper() { + if (handle) { + cudensitymatDestroy(handle); + } +} + cudensitymatWrappedScalarCallback_t -_wrap_callback(const scalar_operator &scalar_op) { +cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { try { std::complex evaluatedValue = scalar_op.evaluate({}); @@ -71,7 +79,7 @@ _wrap_callback(const scalar_operator &scalar_op) { } cudensitymatWrappedTensorCallback_t -_wrap_tensor_callback(const matrix_operator &op) { +cudm_helper::_wrap_tensor_callback(const matrix_operator &op) { auto callback = [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, const int64_t mode_extents[], const int32_t diagonal_offsets[], @@ -131,7 +139,8 @@ _wrap_tensor_callback(const matrix_operator &op) { } // Function to flatten a matrix into a 1D array (column major) -std::vector> flatten_matrix(const matrix_2 &matrix) { +std::vector> +cudm_helper::flatten_matrix(const matrix_2 &matrix) { std::vector> flat_matrix; flat_matrix.reserve(matrix.get_size()); for (size_t col = 0; col < matrix.get_columns(); col++) { @@ -145,8 +154,8 @@ std::vector> flatten_matrix(const matrix_2 &matrix) { // Function to extract sub-space extents based on degrees std::vector -get_subspace_extents(const std::vector &mode_extents, - const std::vector °rees) { +cudm_helper::get_subspace_extents(const std::vector &mode_extents, + const std::vector °rees) { std::vector subspace_extents; for (int degree : degrees) { @@ -161,9 +170,14 @@ get_subspace_extents(const std::vector &mode_extents, // Function to create a cudensitymat elementary operator // Need to use std::variant -cudensitymatElementaryOperator_t create_elementary_operator( - cudensitymatHandle_t handle, const std::vector &subspace_extents, - const std::vector> &flat_matrix) { +cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( + const cudaq::matrix_operator *elem_op, + const std::map> ¶meters, + const std::vector &mode_extents) { + auto subspace_extents = get_subspace_extents(mode_extents, elem_op->degrees); + auto flat_matrix = flatten_matrix( + elem_op->to_matrix(convert_dimensions(mode_extents), parameters)); + if (flat_matrix.empty()) { throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); } @@ -172,29 +186,30 @@ cudensitymatElementaryOperator_t create_elementary_operator( throw std::invalid_argument("subspace_extents cannot be empty."); } + cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = {nullptr, + nullptr}; + if (!parameters.empty()) { + std::cout << "_wrap_tensor_callback\n"; + wrapped_tensor_callback = _wrap_tensor_callback(*elem_op); + } + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; // FIXME: leak (need to track this buffer somewhere and delete **after** the // whole evolve) auto *elementaryMat_d = create_array_gpu(flat_matrix); - cudensitymatStatus_t status = cudensitymatCreateElementaryOperator( + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, elementaryMat_d, {nullptr, nullptr}, &cudm_elem_op); - - if (status != CUDENSITYMAT_STATUS_SUCCESS) { - std::cerr << "Error: Failed to create elementary operator. Status: " - << status << std::endl; - return nullptr; - } + CUDA_C_64F, elementaryMat_d, {nullptr, nullptr}, &cudm_elem_op)); return cudm_elem_op; } // Function to append an elementary operator to a term -void append_elementary_operator_to_term( - cudensitymatHandle_t handle, cudensitymatOperatorTerm_t term, +void cudm_helper::append_elementary_operator_to_term( + cudensitymatOperatorTerm_t term, const cudensitymatElementaryOperator_t &elem_op, const std::vector °rees) { @@ -223,9 +238,8 @@ void append_elementary_operator_to_term( } // Function to create and append a scalar to a term -void append_scalar_to_term(cudensitymatHandle_t handle, - cudensitymatOperatorTerm_t term, - const scalar_operator &scalar_op) { +void cudm_helper::append_scalar_to_term(cudensitymatOperatorTerm_t term, + const scalar_operator &scalar_op) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (!scalar_op.get_generator()) { @@ -241,8 +255,8 @@ void append_scalar_to_term(cudensitymatHandle_t handle, } } -void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, - double scale_factor, cudaStream_t stream) { +void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, + cudaStream_t stream) { if (!state) { throw std::invalid_argument("Invalid state provided to scale_state."); } @@ -253,10 +267,9 @@ void scale_state(cudensitymatHandle_t handle, cudensitymatState_t state, HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); } -cudensitymatOperator_t -compute_lindblad_operator(cudensitymatHandle_t handle, - const std::vector &c_ops, - const std::vector &mode_extents) { +cudensitymatOperator_t cudm_helper::compute_lindblad_operator( + const std::vector &c_ops, + const std::vector &mode_extents) { if (c_ops.empty()) { throw std::invalid_argument("Collapse operators cannot be empty."); } @@ -283,8 +296,8 @@ compute_lindblad_operator(cudensitymatHandle_t handle, mode_extents.data(), &term)); // Create elementary operator from c_op - cudensitymatElementaryOperator_t cudm_elem_op = - create_elementary_operator(handle, mode_extents, flat_matrix); + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; + // create_elementary_operator(c_op, {}, mode_extents); if (!cudm_elem_op) { throw std::runtime_error("Failed to create elementary operator in " @@ -293,7 +306,7 @@ compute_lindblad_operator(cudensitymatHandle_t handle, // Append the elementary operator to the term std::vector degrees = {0}; - append_elementary_operator_to_term(handle, term, cudm_elem_op, degrees); + append_elementary_operator_to_term(term, cudm_elem_op, degrees); // Add term to lindblad operator cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; @@ -318,7 +331,7 @@ compute_lindblad_operator(cudensitymatHandle_t handle, } std::map -convert_dimensions(const std::vector &mode_extents) { +cudm_helper::convert_dimensions(const std::vector &mode_extents) { std::map dimensions; for (size_t i = 0; i < mode_extents.size(); i++) { dimensions[static_cast(i)] = static_cast(mode_extents[i]); @@ -327,8 +340,7 @@ convert_dimensions(const std::vector &mode_extents) { } template -cudensitymatOperator_t convert_to_cudensitymat_operator( - cudensitymatHandle_t handle, +cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( const std::map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { @@ -356,22 +368,11 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( // just call to_matrix on it if (const auto *elem_op = dynamic_cast(&component)) { - auto subspace_extents = - get_subspace_extents(mode_extents, elem_op->degrees); - cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = { - nullptr, nullptr}; - if (!parameters.empty()) { - std::cout << "_wrap_tensor_callback\n"; - wrapped_tensor_callback = _wrap_tensor_callback(*elem_op); - } - - auto flat_matrix = flatten_matrix( - elem_op->to_matrix(convert_dimensions(mode_extents), parameters)); auto cudm_elem_op = - create_elementary_operator(handle, subspace_extents, flat_matrix); + create_elementary_operator(elem_op, parameters, mode_extents); // elementary_operators.push_back(cudm_elem_op); - append_elementary_operator_to_term(handle, term, cudm_elem_op, + append_elementary_operator_to_term(term, cudm_elem_op, elem_op->degrees); } else { // Catch anything that we don't know @@ -417,8 +418,8 @@ cudensitymatOperator_t convert_to_cudensitymat_operator( } } -cudensitymatOperator_t construct_liouvillian( - cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, +cudensitymatOperator_t cudm_helper::construct_liouvillian( + const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, double gamma) { try { @@ -445,7 +446,8 @@ cudensitymatOperator_t construct_liouvillian( } // Function for creating an array copy in GPU memory -void *create_array_gpu(const std::vector> &cpu_array) { +void *cudm_helper::create_array_gpu( + const std::vector> &cpu_array) { void *gpu_array{nullptr}; const std::size_t array_size = cpu_array.size() * sizeof(std::complex); @@ -459,15 +461,15 @@ void *create_array_gpu(const std::vector> &cpu_array) { } // Function to detsroy a previously created array copy in GPU memory -void destroy_array_gpu(void *gpu_array) { +void cudm_helper::destroy_array_gpu(void *gpu_array) { if (gpu_array) { HANDLE_CUDA_ERROR(cudaFree(gpu_array)); } } template cudensitymatOperator_t -convert_to_cudensitymat_operator( - cudensitymatHandle_t, const std::map> &, +cudm_helper::convert_to_cudensitymat_operator( + const std::map> &, const operator_sum &, const std::vector &); } // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index a8eb0bce0b..9c8360c78a 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -76,7 +76,7 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, // Allocate GPU storage for workspace buffer const std::size_t bufferVolume = requiredBufferSize / sizeof(std::complex); - workspaceBuffer = create_array_gpu( + workspaceBuffer = cudm_helper::create_array_gpu( std::vector>(bufferVolume, {0.0, 0.0})); // Attach workspace buffer @@ -93,7 +93,7 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); // Cleanup - destroy_array_gpu(workspaceBuffer); + cudm_helper::destroy_array_gpu(workspaceBuffer); HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); return next_state; diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 989a38c2c3..bf74355823 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -119,12 +119,16 @@ evolve_result evolve_single( std::optional shots_count) { cudensitymatHandle_t handle; HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + + cudm_helper helper(handle); + std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); - auto cudmOp = cudaq::convert_to_cudensitymat_operator( - handle, {}, hamiltonian, dims); - auto time_stepper = std::make_shared(handle, cudmOp); + helper.convert_to_cudensitymat_operator( + {}, hamiltonian, dims); + // Need to pass liouvillian here + auto time_stepper = std::make_shared(handle, nullptr); const std::vector> initialState = {{1.0, 0.0}, {0.0, 0.0}}; auto integrator = std::make_unique( @@ -134,8 +138,8 @@ evolve_result evolve_single( std::vector expectations; for (auto &obs : observables) expectations.emplace_back(cudm_expectation( - handle, cudaq::convert_to_cudensitymat_operator( - handle, {}, *obs, dims))); + handle, helper.convert_to_cudensitymat_operator( + {}, *obs, dims))); for (const auto &step : schedule) { std::cout << "Step: " << step << "\n"; diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index b86e1cb37f..0a59a7a5d4 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -33,11 +33,12 @@ class CuDensityExpectationTest : public ::testing::Test { }; TEST_F(CuDensityExpectationTest, checkCompute) { + cudm_helper helper(handle_); const std::vector dims = {10}; // Check number operator on boson Fock space auto op = cudaq::matrix_operator::number(0); - auto cudmOp = cudaq::convert_to_cudensitymat_operator( - handle_, {}, op, dims); + auto cudmOp = helper.convert_to_cudensitymat_operator( + {}, op, dims); cudm_expectation expectation(handle_, cudmOp); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 0cb213a764..9443d9e559 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -58,6 +58,7 @@ TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { // Test for compute_lindblad_op TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { + cudaq::cudm_helper helper(handle); std::vector mode_extents = {2, 2}; cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); @@ -65,8 +66,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { std::vector c_ops = {c_op1, c_op2}; EXPECT_NO_THROW({ - auto lindblad_op = - cudaq::compute_lindblad_operator(handle, c_ops, mode_extents); + auto lindblad_op = helper.compute_lindblad_operator(c_ops, mode_extents); ASSERT_NE(lindblad_op, nullptr) << "Error: Lindblad operator creation failed!"; @@ -78,14 +78,15 @@ TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { + cudaq::cudm_helper helper(handle); std::vector mode_extents = mock_hilbert_space_dims(); auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ auto result = - cudaq::convert_to_cudensitymat_operator( - handle, {}, op_sum, mode_extents); + helper.convert_to_cudensitymat_operator( + {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); @@ -94,14 +95,15 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { // Test with a higher-dimensional mode extent TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { + cudaq::cudm_helper helper(handle); std::vector mode_extents = {3, 3}; auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ auto result = - cudaq::convert_to_cudensitymat_operator( - handle, {}, op_sum, mode_extents); + helper.convert_to_cudensitymat_operator( + {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -109,6 +111,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { // Test with a coefficient callback function TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { + cudaq::cudm_helper helper(handle); std::vector mode_extents = {2, 2}; auto callback_function = [](std::map>) { @@ -121,8 +124,8 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { EXPECT_NO_THROW({ auto result = - cudaq::convert_to_cudensitymat_operator( - handle, {}, op_sum, mode_extents); + helper.convert_to_cudensitymat_operator( + {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); }); @@ -130,11 +133,12 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { // Test with tensor callback function TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { + cudaq::cudm_helper helper(handle); std::vector mode_extents = {2, 2}; cudaq::matrix_operator matrix_op("CustomOp", {0, 1}); - auto wrapped_tensor_callback = cudaq::_wrap_tensor_callback(matrix_op); + auto wrapped_tensor_callback = helper._wrap_tensor_callback(matrix_op); ASSERT_NE(wrapped_tensor_callback.callback, nullptr); @@ -152,6 +156,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { // Test for appending a scalar to a term TEST_F(CuDensityMatHelpersTestFixture, AppendScalarToTerm) { + cudaq::cudm_helper helper(handle); cudensitymatOperatorTerm_t term; std::vector mode_extents = {2, 2}; @@ -161,47 +166,34 @@ TEST_F(CuDensityMatHelpersTestFixture, AppendScalarToTerm) { cudaq::scalar_operator scalar_op(2.0); - EXPECT_NO_THROW(cudaq::append_scalar_to_term(handle, term, scalar_op)); + EXPECT_NO_THROW(helper.append_scalar_to_term(term, scalar_op)); cudensitymatDestroyOperatorTerm(term); } // Test for appending a matrix_operator -TEST_F(CuDensityMatHelpersTestFixture, AppendElementaryOperatorToTerm) { - cudensitymatOperatorTerm_t term; - std::vector mode_extents = {2, 2}; +// TEST_F(CuDensityMatHelpersTestFixture, AppendElementaryOperatorToTerm) { +// cudaq::cudm_helper helper(handle); +// cudensitymatOperatorTerm_t term; +// std::vector mode_extents = {2, 2}; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &term)); +// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( +// handle, static_cast(mode_extents.size()), mode_extents.data(), +// &term)); - cudaq::matrix_operator matrix_op = mock_matrix_operator("CustomOp", 0); - auto wrapped_tensor_callback = cudaq::_wrap_tensor_callback(matrix_op); - ASSERT_NE(wrapped_tensor_callback.callback, nullptr); +// cudaq::matrix_operator matrix_op = mock_matrix_operator("CustomOp", 0); - auto flat_matrix = cudaq::flatten_matrix( - matrix_op.to_matrix(cudaq::convert_dimensions(mode_extents), {})); - auto subspace_extents = cudaq::get_subspace_extents(mode_extents, {0, 1}); +// auto flat_matrix = helper.flatten_matrix( +// matrix_op.to_matrix(helper.convert_dimensions(mode_extents), {})); +// auto subspace_extents = helper.get_subspace_extents(mode_extents, {0, 1}); - auto elementary_op = - cudaq::create_elementary_operator(handle, subspace_extents, flat_matrix); - ASSERT_NE(elementary_op, nullptr); +// auto elementary_op = +// helper.create_elementary_operator(subspace_extents, flat_matrix); +// ASSERT_NE(elementary_op, nullptr); - EXPECT_NO_THROW(cudaq::append_elementary_operator_to_term( - handle, term, elementary_op, {0, 1})); +// EXPECT_NO_THROW(helper.append_elementary_operator_to_term( +// term, elementary_op, {0, 1})); - cudensitymatDestroyOperatorTerm(term); - cudensitymatDestroyElementaryOperator(elementary_op); -} - -// Test invalid handle -TEST_F(CuDensityMatHelpersTestFixture, InvalidHandle) { - cudensitymatHandle_t invalid_handle = nullptr; - - std::vector mode_extents = {2, 2}; - auto op_sum = initialize_operator_sum(); - - EXPECT_THROW(cudaq::convert_to_cudensitymat_operator( - invalid_handle, {}, op_sum, mode_extents), - std::runtime_error); -} +// cudensitymatDestroyOperatorTerm(term); +// cudensitymatDestroyElementaryOperator(elementary_op); +// } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index c84555ebed..914182466d 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -93,13 +93,14 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { } TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { + cudm_helper helper(handle_); const std::vector> initialState = { {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {4}; auto inputState = std::make_unique(handle_, initialState, dims); auto op = cudaq::matrix_operator::create(0); - auto cudmOp = cudaq::convert_to_cudensitymat_operator( - handle_, {}, op, dims); // Initialize the time stepper + auto cudmOp = helper.convert_to_cudensitymat_operator( + {}, op, dims); // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); auto outputState = time_stepper->compute(*inputState, 0.0, 1.0); diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 1e5de6165c..d596bf507f 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -149,6 +149,7 @@ TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { } TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { + cudm_helper helper(handle_); const std::vector> initialState = {{1.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {2}; @@ -172,8 +173,8 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, cudaq::matrix_operator(op_id, {0})); auto cudmOp = - cudaq::convert_to_cudensitymat_operator( - handle_, {}, op, dims); + helper.convert_to_cudensitymat_operator({}, op, + dims); auto time_stepper = std::make_shared(handle_, cudmOp); auto eulerIntegrator = std::make_unique( From 7a2c7e7891eec74279dca2afbfcc9bfe0fcd0d2c Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 11 Feb 2025 14:55:57 -0800 Subject: [PATCH 241/311] replacing tensor callback parameter Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/cudm_helpers.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 50fc655e8e..0ed93610b8 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -202,7 +202,7 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, elementaryMat_d, {nullptr, nullptr}, &cudm_elem_op)); + CUDA_C_64F, elementaryMat_d, wrapped_tensor_callback, &cudm_elem_op)); return cudm_elem_op; } From 2c74fa237494db60a13f5a304e835fb3c1c2de53 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Wed, 12 Feb 2025 00:19:59 +0000 Subject: [PATCH 242/311] End-end evolve test Signed-off-by: Thien Nguyen --- runtime/common/EvolveResult.h | 4 +-- runtime/cudaq/cudm_expectation.h | 8 +++++ runtime/cudaq/dynamics/evolution.cpp | 35 +++++++++++++++++----- runtime/cudaq/evolution.h | 2 +- runtime/cudaq/qis/state.cpp | 2 +- unittests/dynamics/test_evolve_single.cpp | 36 +++++++++++++++++++++-- 6 files changed, 72 insertions(+), 15 deletions(-) diff --git a/runtime/common/EvolveResult.h b/runtime/common/EvolveResult.h index 06382f4ff6..c254e3af18 100644 --- a/runtime/common/EvolveResult.h +++ b/runtime/common/EvolveResult.h @@ -108,8 +108,8 @@ class evolve_result { return final_expectation_values; } - std::optional>> - get_expectation_values() { + const std::optional>> & + get_expectation_values() const { return expectation_values; } diff --git a/runtime/cudaq/cudm_expectation.h b/runtime/cudaq/cudm_expectation.h index 88e2a8ff50..b6db4c0c76 100644 --- a/runtime/cudaq/cudm_expectation.h +++ b/runtime/cudaq/cudm_expectation.h @@ -19,6 +19,14 @@ class cudm_expectation { public: cudm_expectation(cudensitymatHandle_t handle, cudensitymatOperator_t op); + cudm_expectation(const cudm_expectation &) = delete; + cudm_expectation &operator=(const cudm_expectation &) = delete; + cudm_expectation(cudm_expectation &&src) { + std::swap(m_handle, src.m_handle); + std::swap(m_hamOp, src.m_hamOp); + std::swap(m_expectation, src.m_expectation); + std::swap(m_workspace, src.m_workspace); + } ~cudm_expectation(); void prepare(cudensitymatState_t state); std::complex compute(cudensitymatState_t state, double time); diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index bf74355823..0b3ed64006 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -110,7 +110,7 @@ namespace cudaq { evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, - state initial_state, + const state &initial_state, const std::vector *> &collapse_operators, const std::vector *> &observables, @@ -125,10 +125,10 @@ evolve_result evolve_single( std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); - helper.convert_to_cudensitymat_operator( + auto liouvillian = helper.convert_to_cudensitymat_operator( {}, hamiltonian, dims); // Need to pass liouvillian here - auto time_stepper = std::make_shared(handle, nullptr); + auto time_stepper = std::make_shared(handle, liouvillian); const std::vector> initialState = {{1.0, 0.0}, {0.0, 0.0}}; auto integrator = std::make_unique( @@ -141,18 +141,37 @@ evolve_result evolve_single( handle, helper.convert_to_cudensitymat_operator( {}, *obs, dims))); + std::vector> expectationVals; for (const auto &step : schedule) { std::cout << "Step: " << step << "\n"; integrator->integrate(step); auto [t, currentState] = integrator->get_state(); - for (auto &expectation : expectations) { - const auto expVal = expectation.compute(currentState.get_impl(), step); - std::cout << "Expectation value = " << expVal << "\n"; + if (store_intermediate_results) { + std::vector expVals; + for (auto &expectation : expectations) { + expectation.prepare(currentState.get_impl()); + const auto expVal = expectation.compute(currentState.get_impl(), step); + expVals.emplace_back(expVal.real()); + } + expectationVals.emplace_back(std::move(expVals)); } } - // TODO - return evolve_result(sample_result()); + if (store_intermediate_results) { + // TODO: need to convert to proper state + return evolve_result({initial_state}, expectationVals); + } else { + // Only final state is needed + auto [finalTime, finalState] = integrator->get_state(); + std::vector expVals; + for (auto &expectation : expectations) { + expectation.prepare(finalState.get_impl()); + const auto expVal = expectation.compute(finalState.get_impl(), finalTime); + expVals.emplace_back(expVal.real()); + } + // TODO: need to convert to proper state + return evolve_result(initial_state, expVals); + } } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 27100bc494..87b315de7d 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -24,7 +24,7 @@ namespace cudaq { evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, - state initial_state, + const state &initial_state, const std::vector *> &collapse_operators = {}, const std::vector *> &observables = {}, diff --git a/runtime/cudaq/qis/state.cpp b/runtime/cudaq/qis/state.cpp index d5f9240119..717ca94272 100644 --- a/runtime/cudaq/qis/state.cpp +++ b/runtime/cudaq/qis/state.cpp @@ -119,7 +119,7 @@ state::~state() { // Current use count is 1, so the // shared_ptr is about to go out of scope, // there are no users. Delete the state data. - if (internal.use_count() == 1) + if (internal && internal.use_count() == 1) internal->destroyState(); } diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index b28392a0d0..60c8728956 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -29,9 +29,39 @@ TEST(EvolveTester, checkSimple) { auto ham = cudaq::product_operator( std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, cudaq::matrix_operator(op_id, {0})); - cudaq::Schedule schedule({0.0, 0.1, 0.2}); + constexpr int numSteps = 10; + cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); + + cudaq::matrix_operator::define( + "pauli_z", {-1}, + [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = cudaq::matrix_2(2, 2); + mat[{0, 0}] = 1.0; + mat[{1, 1}] = -1.0; + return mat; + }); + auto pauliZ = cudaq::product_operator( + std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); auto initialState = cudaq::state::from_data(std::vector>{1.0, 0.0}); - auto result = - cudaq::evolve_single(ham, dims, schedule, initialState, {}, {&ham}); + auto result = cudaq::evolve_single(ham, dims, schedule, initialState, {}, + {&pauliZ}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + for (const auto &t : schedule) { + const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); + theoryResults.emplace_back(expected); + } + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } } From 3e811448a01d8fa82f68c54fa232f026a8ac2b52 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 11 Feb 2025 20:56:11 -0800 Subject: [PATCH 243/311] Fixing lindblad operator Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 9 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 141 ++++++++++++++++++----- runtime/cudaq/dynamics/evolution.cpp | 5 +- runtime/cudaq/operators.h | 2 +- runtime/cudaq/utils/tensor.cpp | 15 +++ runtime/cudaq/utils/tensor.h | 3 + unittests/dynamics/test_cudm_helpers.cpp | 108 ++++++++--------- 7 files changed, 190 insertions(+), 93 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 3bf2909431..46bde020dd 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -23,7 +23,8 @@ class cudm_helper { ~cudm_helper(); // Matrix flattening - std::vector> flatten_matrix(const matrix_2 &matrix); + static std::vector> + flatten_matrix(const matrix_2 &matrix); // State Operations void scale_state(cudensitymatState_t state, double scale_factor, @@ -55,16 +56,16 @@ class cudm_helper { const std::vector °rees); // Callback Wrappers - cudensitymatWrappedScalarCallback_t + static cudensitymatWrappedScalarCallback_t _wrap_callback(const scalar_operator &scalar_op); - cudensitymatWrappedTensorCallback_t + static cudensitymatWrappedTensorCallback_t _wrap_tensor_callback(const matrix_operator &op); // Elementary Operator Functions void append_scalar_to_term(cudensitymatOperatorTerm_t term, const scalar_operator &scalar_op); cudensitymatElementaryOperator_t create_elementary_operator( - const cudaq::matrix_operator *elem_op, + const cudaq::matrix_operator &elem_op, const std::map> ¶meters, const std::vector &mode_extents); void append_elementary_operator_to_term( diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 0ed93610b8..2b3a60022f 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -17,7 +17,10 @@ cudm_helper::cudm_helper(cudensitymatHandle_t handle) : handle(handle) {} cudm_helper::~cudm_helper() { if (handle) { cudensitymatDestroy(handle); + handle = nullptr; } + + cudaDeviceSynchronize(); } cudensitymatWrappedScalarCallback_t @@ -171,12 +174,12 @@ cudm_helper::get_subspace_extents(const std::vector &mode_extents, // Function to create a cudensitymat elementary operator // Need to use std::variant cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( - const cudaq::matrix_operator *elem_op, + const cudaq::matrix_operator &elem_op, const std::map> ¶meters, const std::vector &mode_extents) { - auto subspace_extents = get_subspace_extents(mode_extents, elem_op->degrees); + auto subspace_extents = get_subspace_extents(mode_extents, elem_op.degrees); auto flat_matrix = flatten_matrix( - elem_op->to_matrix(convert_dimensions(mode_extents), parameters)); + elem_op.to_matrix(convert_dimensions(mode_extents), parameters)); if (flat_matrix.empty()) { throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); @@ -188,22 +191,29 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = {nullptr, nullptr}; + if (!parameters.empty()) { - std::cout << "_wrap_tensor_callback\n"; - wrapped_tensor_callback = _wrap_tensor_callback(*elem_op); - } - cudensitymatElementaryOperator_t cudm_elem_op = nullptr; + wrapped_tensor_callback = _wrap_tensor_callback(elem_op); + } // FIXME: leak (need to track this buffer somewhere and delete **after** the // whole evolve) auto *elementaryMat_d = create_array_gpu(flat_matrix); + cudensitymatElementaryOperator_t cudm_elem_op = nullptr; HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( handle, static_cast(subspace_extents.size()), subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, elementaryMat_d, wrapped_tensor_callback, &cudm_elem_op)); + if (!cudm_elem_op) { + std::cerr << "[ERROR] cudm_elem_op is NULL in create_elementary_operator!" + << std::endl; + destroy_array_gpu(elementaryMat_d); + throw std::runtime_error("Failed to create elementary operator."); + } + return cudm_elem_op; } @@ -267,6 +277,7 @@ void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); } +// c_ops: std::vector cudensitymatOperator_t cudm_helper::compute_lindblad_operator( const std::vector &c_ops, const std::vector &mode_extents) { @@ -279,63 +290,135 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &lindblad_op)); + std::vector terms; + std::vector elem_ops; + try { for (const auto &c_op : c_ops) { + size_t dim = c_op.get_rows(); + if (dim == 0 || c_op.get_columns() != dim) { throw std::invalid_argument( "Collapse operator must be a square matrix."); } - auto flat_matrix = flatten_matrix(c_op); + matrix_2 L_dagger_op_matrix = matrix_2::adjoint(c_op); - // Create Operator term for LtL and add to lindblad_op - cudensitymatOperatorTerm_t term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term)); + const std::string L_id = "L_op"; + const std::string L_dagger_id = "L_dagger_op"; + + cudaq::matrix_operator::define( + L_id, {-1}, + [c_op](std::vector dims, + std::map>) { return c_op; }); + + cudaq::matrix_operator::define( + L_dagger_id, {-1}, + [L_dagger_op_matrix](std::vector dims, + std::map>) { + return L_dagger_op_matrix; + }); + + matrix_operator L_op(L_id, {0}); + matrix_operator L_dagger_op(L_dagger_id, {0}); - // Create elementary operator from c_op - cudensitymatElementaryOperator_t cudm_elem_op = nullptr; - // create_elementary_operator(c_op, {}, mode_extents); + cudensitymatElementaryOperator_t L_elem_op = + create_elementary_operator(L_op, {}, mode_extents); - if (!cudm_elem_op) { - throw std::runtime_error("Failed to create elementary operator in " + cudensitymatElementaryOperator_t L_dagger_elem_op = + create_elementary_operator(L_dagger_op, {}, mode_extents); + + if (!L_elem_op || !L_dagger_elem_op) { + throw std::runtime_error("Failed to create elementary operators in " "compute_lindblad_operator."); } - // Append the elementary operator to the term - std::vector degrees = {0}; - append_elementary_operator_to_term(term, cudm_elem_op, degrees); + elem_ops.push_back(L_elem_op); + elem_ops.push_back(L_dagger_elem_op); + + // D1 = L * Lt + cudensitymatOperatorTerm_t term_D1; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term_D1)); + + append_elementary_operator_to_term(term_D1, L_elem_op, {0}); + + append_elementary_operator_to_term(term_D1, L_dagger_elem_op, {0}); - // Add term to lindblad operator - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; + // Add term D1 to the Lindblad operator + + cudensitymatWrappedScalarCallback_t scalar_callback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, lindblad_op, term, 0, make_cuDoubleComplex(1.0, 0.0), - scalarCallback)); + handle, lindblad_op, term_D1, 0, make_cuDoubleComplex(1.0, 0.0), + scalar_callback)); - // Destroy intermediate resources - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(cudm_elem_op)); + // D2 = -0.5 * (Lt * L) + cudensitymatOperatorTerm_t term_D2; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term_D2)); + + append_elementary_operator_to_term(term_D2, L_dagger_elem_op, {0}); + + append_elementary_operator_to_term(term_D2, L_elem_op, {0}); + + // Add term D2 to the Lindblad operator + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, lindblad_op, term_D2, 0, make_cuDoubleComplex(-0.5, 0.0), + scalar_callback)); + + // D3 = -0.5 * (L * Lt) + cudensitymatOperatorTerm_t term_D3; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), + mode_extents.data(), &term_D3)); + append_elementary_operator_to_term(term_D3, L_elem_op, {0}); + append_elementary_operator_to_term(term_D3, L_dagger_elem_op, {0}); + + // Add term D3 to the Lindblad operator + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, lindblad_op, term_D3, 0, make_cuDoubleComplex(-0.5, 0.0), + scalar_callback)); } HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); } catch (const std::exception &e) { std::cerr << "Exception in compute_lindblad_operator: " << e.what() << std::endl; + + for (auto term : terms) { + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + } + + for (auto elem_op : elem_ops) { + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + } + cudensitymatDestroyOperator(lindblad_op); return nullptr; } + for (auto term : terms) { + HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + } + + for (auto elem_op : elem_ops) { + HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + } + return lindblad_op; } std::map cudm_helper::convert_dimensions(const std::vector &mode_extents) { + std::map dimensions; for (size_t i = 0; i < mode_extents.size(); i++) { dimensions[static_cast(i)] = static_cast(mode_extents[i]); } + return dimensions; } @@ -369,7 +452,7 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( if (const auto *elem_op = dynamic_cast(&component)) { auto cudm_elem_op = - create_elementary_operator(elem_op, parameters, mode_extents); + create_elementary_operator(*elem_op, parameters, mode_extents); // elementary_operators.push_back(cudm_elem_op); append_elementary_operator_to_term(term, cudm_elem_op, diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 0b3ed64006..684d38d08f 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -125,8 +125,9 @@ evolve_result evolve_single( std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); - auto liouvillian = helper.convert_to_cudensitymat_operator( - {}, hamiltonian, dims); + auto liouvillian = + helper.convert_to_cudensitymat_operator( + {}, hamiltonian, dims); // Need to pass liouvillian here auto time_stepper = std::make_shared(handle, liouvillian); const std::vector> initialState = {{1.0, 0.0}, diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index baa9e7bee6..55c7e184de 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -625,7 +625,7 @@ class matrix_operator { auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { // todo: make a nice error message to say op already exists - throw; + // throw; } } }; diff --git a/runtime/cudaq/utils/tensor.cpp b/runtime/cudaq/utils/tensor.cpp index 5298067e9d..f556c04357 100644 --- a/runtime/cudaq/utils/tensor.cpp +++ b/runtime/cudaq/utils/tensor.cpp @@ -197,3 +197,18 @@ cudaq::matrix_2 cudaq::matrix_2::identity(const std::size_t rows) { result[{i, i}] = 1. + 0.0j; return result; } + +// Transpose + Conjugate +cudaq::matrix_2 cudaq::matrix_2::adjoint(const matrix_2 &matrix) { + std::size_t rows = matrix.get_rows(); + std::size_t cols = matrix.get_columns(); + matrix_2 result(cols, rows); + + for (std::size_t i = 0; i < rows; i++) { + for (std::size_t j = 0; j < cols; j++) { + result[{j, i}] = std::conj(matrix[{i, j}]); + } + } + + return result; +} \ No newline at end of file diff --git a/runtime/cudaq/utils/tensor.h b/runtime/cudaq/utils/tensor.h index 25518fa4a7..801f54ab5b 100644 --- a/runtime/cudaq/utils/tensor.h +++ b/runtime/cudaq/utils/tensor.h @@ -107,6 +107,9 @@ class matrix_2 { /// Matrix power. matrix_2 power(int powers); + /// Returns the conjugate transpose of a matrix. + static matrix_2 adjoint(const matrix_2 &matrix); + /// Return a square identity matrix for the given size. static matrix_2 identity(const std::size_t rows); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 9443d9e559..9bce633f89 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -22,13 +22,20 @@ class CuDensityMatHelpersTestFixture : public ::testing::Test { protected: cudensitymatHandle_t handle; cudaStream_t stream; + std::unique_ptr helper; + std::unique_ptr state; void SetUp() override { HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); stream = 0; + helper = std::make_unique(handle); + + std::vector mode_extents = {2}; + std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; + state = std::make_unique(handle, rawData, mode_extents); } - void TearDown() override { cudensitymatDestroy(handle); } + void TearDown() override { HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); } }; // Test for initialize_state @@ -44,48 +51,53 @@ TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { // Test for scale_state // TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { -// std::vector mode_extents = {2}; - -// std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - -// cudaq::cudm_state state(handle, rawData, mode_extents); +// ASSERT_TRUE(state->is_initialized()); -// ASSERT_TRUE(state.is_initialized()); - -// EXPECT_NO_THROW(cudaq::scale_state(handle, state.get_impl(), 2.0, +// EXPECT_NO_THROW(helper->scale_state(state->get_impl(), 2.0, // stream)); // } // Test for compute_lindblad_op TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { - cudaq::cudm_helper helper(handle); std::vector mode_extents = {2, 2}; - cudaq::matrix_2 c_op1({1.0, 0.0, 0.0, 0.0}, {2, 2}); - cudaq::matrix_2 c_op2({0.0, 0.0, 0.0, 1.0}, {2, 2}); + std::vector> c_op1_values = { + {1.0, 0.0}, + {0.0, 0.0}, + {0.0, 0.0}, + {0.0, 0.0}, + }; + + std::vector> c_op2_values = { + {0.0, 0.0}, + {0.0, 1.0}, + {0.0, 0.0}, + {0.0, 0.0}, + }; + + cudaq::matrix_2 c_op1(c_op1_values, {2, 2}); + cudaq::matrix_2 c_op2(c_op2_values, {2, 2}); std::vector c_ops = {c_op1, c_op2}; EXPECT_NO_THROW({ - auto lindblad_op = helper.compute_lindblad_operator(c_ops, mode_extents); + auto lindblad_op = helper->compute_lindblad_operator(c_ops, mode_extents); + ASSERT_NE(lindblad_op, nullptr) << "Error: Lindblad operator creation failed!"; - HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); - cudensitymatDestroyOperator(lindblad_op); }); } // Test for convert_to_cudensitymat_operator TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { - cudaq::cudm_helper helper(handle); std::vector mode_extents = mock_hilbert_space_dims(); auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ auto result = - helper.convert_to_cudensitymat_operator( + helper->convert_to_cudensitymat_operator( {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); @@ -95,14 +107,13 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { // Test with a higher-dimensional mode extent TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { - cudaq::cudm_helper helper(handle); std::vector mode_extents = {3, 3}; auto op_sum = initialize_operator_sum(); EXPECT_NO_THROW({ auto result = - helper.convert_to_cudensitymat_operator( + helper->convert_to_cudensitymat_operator( {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); @@ -111,7 +122,6 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { // Test with a coefficient callback function TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { - cudaq::cudm_helper helper(handle); std::vector mode_extents = {2, 2}; auto callback_function = [](std::map>) { @@ -124,7 +134,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { EXPECT_NO_THROW({ auto result = - helper.convert_to_cudensitymat_operator( + helper->convert_to_cudensitymat_operator( {}, op_sum, mode_extents); ASSERT_NE(result, nullptr); cudensitymatDestroyOperator(result); @@ -133,22 +143,34 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { // Test with tensor callback function TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { - cudaq::cudm_helper helper(handle); std::vector mode_extents = {2, 2}; - cudaq::matrix_operator matrix_op("CustomOp", {0, 1}); + const std::string op_id = "custom_op"; + auto func = [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = cudaq::matrix_2(2, 2); + mat[{1, 0}] = 1.0; + mat[{0, 1}] = 1.0; + return mat; + }; + cudaq::matrix_operator::define(op_id, {-1}, func); + cudaq::matrix_operator matrix_op(op_id, {0}); - auto wrapped_tensor_callback = helper._wrap_tensor_callback(matrix_op); + auto wrapped_tensor_callback = + cudaq::cudm_helper::_wrap_tensor_callback(matrix_op); ASSERT_NE(wrapped_tensor_callback.callback, nullptr); - // auto op_sum = cudaq::operator_sum(matrix_op) + - // matrix_op; + // auto op_sum = matrix_op + matrix_op; // EXPECT_NO_THROW({ // auto result = - // cudaq::convert_to_cudensitymat_operator( - // handle, {}, op_sum, mode_extents); + // helper->convert_to_cudensitymat_operator( + // {}, op_sum, mode_extents); // ASSERT_NE(result, nullptr); // cudensitymatDestroyOperator(result); // }); @@ -156,7 +178,6 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { // Test for appending a scalar to a term TEST_F(CuDensityMatHelpersTestFixture, AppendScalarToTerm) { - cudaq::cudm_helper helper(handle); cudensitymatOperatorTerm_t term; std::vector mode_extents = {2, 2}; @@ -166,34 +187,7 @@ TEST_F(CuDensityMatHelpersTestFixture, AppendScalarToTerm) { cudaq::scalar_operator scalar_op(2.0); - EXPECT_NO_THROW(helper.append_scalar_to_term(term, scalar_op)); + EXPECT_NO_THROW(helper->append_scalar_to_term(term, scalar_op)); cudensitymatDestroyOperatorTerm(term); } - -// Test for appending a matrix_operator -// TEST_F(CuDensityMatHelpersTestFixture, AppendElementaryOperatorToTerm) { -// cudaq::cudm_helper helper(handle); -// cudensitymatOperatorTerm_t term; -// std::vector mode_extents = {2, 2}; - -// HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( -// handle, static_cast(mode_extents.size()), mode_extents.data(), -// &term)); - -// cudaq::matrix_operator matrix_op = mock_matrix_operator("CustomOp", 0); - -// auto flat_matrix = helper.flatten_matrix( -// matrix_op.to_matrix(helper.convert_dimensions(mode_extents), {})); -// auto subspace_extents = helper.get_subspace_extents(mode_extents, {0, 1}); - -// auto elementary_op = -// helper.create_elementary_operator(subspace_extents, flat_matrix); -// ASSERT_NE(elementary_op, nullptr); - -// EXPECT_NO_THROW(helper.append_elementary_operator_to_term( -// term, elementary_op, {0, 1})); - -// cudensitymatDestroyOperatorTerm(term); -// cudensitymatDestroyElementaryOperator(elementary_op); -// } From 47bfb802d22c0915164f179e51d16bdc4bda4695 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 11 Feb 2025 21:26:59 -0800 Subject: [PATCH 244/311] reverting accidental change to the logic handling already existing matrix operator Signed-off-by: Sachin Pisal --- runtime/cudaq/operators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 55c7e184de..baa9e7bee6 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -625,7 +625,7 @@ class matrix_operator { auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { // todo: make a nice error message to say op already exists - // throw; + throw; } } }; From 1e1b44e4a921dd45b5f5634693af485161676d35 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Wed, 12 Feb 2025 07:57:48 +0000 Subject: [PATCH 245/311] Add composite system test and fix an issue with create a product term from elementary ops Signed-off-by: Thien Nguyen --- runtime/cudaq/cudm_expectation.h | 8 +- runtime/cudaq/cudm_helpers.h | 13 ++- runtime/cudaq/cudm_state.h | 21 +++- runtime/cudaq/dynamics/cudm_expectation.cpp | 6 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 84 ++++++++++---- runtime/cudaq/dynamics/cudm_state.cpp | 110 +++++++++++++++++-- runtime/cudaq/dynamics/cudm_time_stepper.cpp | 5 +- runtime/cudaq/dynamics/evolution.cpp | 13 +-- unittests/dynamics/test_cudm_expectation.cpp | 32 ++++++ unittests/dynamics/test_evolve_single.cpp | 51 ++++++++- 10 files changed, 286 insertions(+), 57 deletions(-) diff --git a/runtime/cudaq/cudm_expectation.h b/runtime/cudaq/cudm_expectation.h index b6db4c0c76..0dfe3a60ec 100644 --- a/runtime/cudaq/cudm_expectation.h +++ b/runtime/cudaq/cudm_expectation.h @@ -12,10 +12,10 @@ namespace cudaq { class cudm_expectation { - cudensitymatHandle_t m_handle; - cudensitymatOperator_t m_hamOp; - cudensitymatExpectation_t m_expectation; - cudensitymatWorkspaceDescriptor_t m_workspace; + cudensitymatHandle_t m_handle{nullptr}; + cudensitymatOperator_t m_hamOp{nullptr}; + cudensitymatExpectation_t m_expectation{nullptr}; + cudensitymatWorkspaceDescriptor_t m_workspace{nullptr}; public: cudm_expectation(cudensitymatHandle_t handle, cudensitymatOperator_t op); diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 46bde020dd..2f807e8b6c 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -42,6 +42,15 @@ class cudm_helper { const operator_sum &op, const std::vector &mode_extents); + // Construct Liouvillian + cudensitymatOperator_t construct_liouvillian( + const operator_sum &op, + const std::vector *> + &collapse_operators, + const std::vector &mode_extents, + const std::map> ¶meters, + bool is_master_equation); + // Construct Liouvillian cudensitymatOperator_t construct_liouvillian( const cudensitymatOperator_t &hamiltonian, @@ -70,8 +79,8 @@ class cudm_helper { const std::vector &mode_extents); void append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, - const cudensitymatElementaryOperator_t &elem_op, - const std::vector °rees); + const std::vector &elem_ops, + const std::vector> °rees); // GPU memory management static void * diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index 8dff591bc7..c72494cf62 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -28,6 +28,13 @@ class cudm_state { const std::vector> rawData, const std::vector &hilbertSpaceDims); + /// @brief To initialize state from a `cudaq::state` + explicit cudm_state(cudensitymatHandle_t handle, const cudaq::state &simState, + const std::vector &hilbertSpaceDims); + + // @brief Create a zero state + static cudm_state zero_like(const cudm_state &other); + // Prevent copies (avoids double free issues) cudm_state(const cudm_state &) = delete; cudm_state &operator=(const cudm_state &) = delete; @@ -102,12 +109,16 @@ class cudm_state { cudm_state operator*(double scalar) const; private: + // TODO: remove this host raw data, we shouldn't keep this as it will be + // decoupled to the GPU data. std::vector> rawData_; + int64_t gpuDataSize_ = 0; std::complex *gpuData_; cudensitymatState_t state_; cudensitymatHandle_t handle_; std::vector hilbertSpaceDims_; - + // Private default constructor + cudm_state() = default; /// @brief Attach raw data storage to GPU void attach_storage(); @@ -115,15 +126,15 @@ class cudm_state { /// dimensions. /// @param hilbertSpaceDims Hilbert space dimensions. /// @return Size of the state vector. - size_t calculate_state_vector_size( - const std::vector &hilbertSpaceDims) const; + static size_t calculate_state_vector_size( + const std::vector &hilbertSpaceDims); /// @brief Calculate the size of the density matrix for the given Hilbert /// space dimensions. /// @param hilbertSpaceDims Hilbert space dimensions. /// @return Size of the density matrix. - size_t calculate_density_matrix_size( - const std::vector &hilbertSpaceDims) const; + static size_t + calculate_density_matrix_size(const std::vector &hilbertSpaceDims); }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/cudm_expectation.cpp b/runtime/cudaq/dynamics/cudm_expectation.cpp index 441666f2df..2705b648b4 100644 --- a/runtime/cudaq/dynamics/cudm_expectation.cpp +++ b/runtime/cudaq/dynamics/cudm_expectation.cpp @@ -22,8 +22,10 @@ cudm_expectation::cudm_expectation(cudensitymatHandle_t handle, } cudm_expectation::~cudm_expectation() { - cudensitymatDestroyWorkspace(m_workspace); - cudensitymatDestroyExpectation(m_expectation); + if (m_workspace) + cudensitymatDestroyWorkspace(m_workspace); + if (m_expectation) + cudensitymatDestroyExpectation(m_expectation); } void cudm_expectation::prepare(cudensitymatState_t state) { diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 2b3a60022f..e992cf2971 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -220,30 +220,48 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( // Function to append an elementary operator to a term void cudm_helper::append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, - const cudensitymatElementaryOperator_t &elem_op, - const std::vector °rees) { + const std::vector &elem_ops, + const std::vector> °rees) { if (degrees.empty()) { throw std::invalid_argument("Degrees vector cannot be empty."); } - if (!elem_op) { - throw std::invalid_argument("elem_op cannot be null."); + if (elem_ops.empty()) { + throw std::invalid_argument("elem_ops cannot be null."); } - for (int degree : degrees) { - if (degree < 0) { - throw std::out_of_range("Degree cannot be negative!"); + if (degrees.size() != elem_ops.size()) { + throw std::invalid_argument( + "elem_ops and degrees must have the same size."); + } + + std::vector allDegrees; + std::vector allModeActionDuality; + for (const auto &sub_degrees : degrees) { + if (sub_degrees.size() != 1) { + throw std::runtime_error( + "Elementary operator must act on a single degree."); + } + for (int degree : sub_degrees) { + if (degree < 0) { + throw std::out_of_range("Degree cannot be negative!"); + } + allDegrees.emplace_back(degree); + allModeActionDuality.emplace_back(0); // left side } } - std::vector elem_ops = {elem_op}; + // std::cout << "append_elementary_operator_to_term: " << elem_op << " -> " << + // term << ": degree = "; for (int degree : degrees) { + // std::cout << degree << " "; + // } + // std::cout << "\n"; - std::vector modeActionDuality(degrees.size(), 0); assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, static_cast(degrees.size()), elem_ops.data(), - degrees.data(), modeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), + handle, term, static_cast(allDegrees.size()), elem_ops.data(), + allDegrees.data(), allModeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); } @@ -343,9 +361,9 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D1)); - append_elementary_operator_to_term(term_D1, L_elem_op, {0}); + append_elementary_operator_to_term(term_D1, {L_elem_op}, {{0}}); - append_elementary_operator_to_term(term_D1, L_dagger_elem_op, {0}); + append_elementary_operator_to_term(term_D1, {L_dagger_elem_op}, {{0}}); // Add term D1 to the Lindblad operator @@ -360,9 +378,9 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D2)); - append_elementary_operator_to_term(term_D2, L_dagger_elem_op, {0}); + append_elementary_operator_to_term(term_D2, {L_dagger_elem_op}, {{0}}); - append_elementary_operator_to_term(term_D2, L_elem_op, {0}); + append_elementary_operator_to_term(term_D2, {L_elem_op}, {{0}}); // Add term D2 to the Lindblad operator HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( @@ -374,8 +392,8 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D3)); - append_elementary_operator_to_term(term_D3, L_elem_op, {0}); - append_elementary_operator_to_term(term_D3, L_dagger_elem_op, {0}); + append_elementary_operator_to_term(term_D3, {L_elem_op}, {{0}}); + append_elementary_operator_to_term(term_D3, {L_dagger_elem_op}, {{0}}); // Add term D3 to the Lindblad operator HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( @@ -441,11 +459,16 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( for (const auto &product_op : op.get_terms()) { cudensitymatOperatorTerm_t term; - + // std::cout << "mode_extent: "; + // for (const auto& mode_extent: mode_extents) + // std::cout << mode_extent << " "; + // std::cout << "\n"; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); + std::vector elem_ops; + std::vector> all_degrees; for (const auto &component : product_op.get_terms()) { // No need to check type // just call to_matrix on it @@ -453,16 +476,14 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( dynamic_cast(&component)) { auto cudm_elem_op = create_elementary_operator(*elem_op, parameters, mode_extents); - - // elementary_operators.push_back(cudm_elem_op); - append_elementary_operator_to_term(term, cudm_elem_op, - elem_op->degrees); + elem_ops.emplace_back(cudm_elem_op); + all_degrees.emplace_back(elem_op->degrees); } else { // Catch anything that we don't know throw std::runtime_error("Unhandled type!"); } } - + append_elementary_operator_to_term(term, elem_ops, all_degrees); // Handle the coefficient // Static value without parameter: as it is // Static value with parameter: Callback @@ -471,6 +492,7 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( if (!coeff.get_generator()) { const auto coeffVal = coeff.evaluate(); + // std::cout << "Append product term " << term << " with coeff = " << coeffVal << " to operator " << operator_handle << "\n"; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle, operator_handle, term, 0, make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), @@ -501,6 +523,22 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( } } +cudensitymatOperator_t cudm_helper::construct_liouvillian( + const operator_sum &op, + const std::vector *> + &collapse_operators, + const std::vector &mode_extents, + const std::map> ¶meters, + bool is_master_equation) { + if (!is_master_equation && collapse_operators.empty()) { + auto liouvillian = op * std::complex(0.0, -1.0); + return convert_to_cudensitymat_operator(parameters, liouvillian, + mode_extents); + } else { + throw std::runtime_error("TODO: handle Lindblad equation"); + } +} + cudensitymatOperator_t cudm_helper::construct_liouvillian( const cudensitymatOperator_t &hamiltonian, const std::vector &collapse_operators, diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/cudaq/dynamics/cudm_state.cpp index 281ee5acce..f52eefcfbb 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/cudaq/dynamics/cudm_state.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -15,12 +16,104 @@ #include namespace cudaq { +cudm_state::cudm_state(cudensitymatHandle_t handle, + const cudaq::state &simState, + const std::vector &hilbertSpaceDims) + : handle_(handle), state_(nullptr), hilbertSpaceDims_(hilbertSpaceDims) { + if (!simState.is_on_gpu()) + throw std::runtime_error("Unexpected state. This must be a state created " + "by the dynamics target"); + const bool isDensityMat = simState.get_tensor().extents.size() == 2; + + gpuDataSize_ = isDensityMat ? calculate_density_matrix_size(hilbertSpaceDims) + : calculate_state_vector_size(hilbertSpaceDims); + + const size_t dataSize = gpuDataSize_ * sizeof(std::complex); + HANDLE_CUDA_ERROR(cudaMalloc(reinterpret_cast(&gpuData_), dataSize)); + + HANDLE_CUDA_ERROR(cudaMemcpy(gpuData_, simState.get_tensor().data, dataSize, + cudaMemcpyDefault)); + + const cudensitymatStatePurity_t purity = isDensityMat + ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + HANDLE_CUDM_ERROR(cudensitymatCreateState( + handle_, purity, static_cast(hilbertSpaceDims.size()), + hilbertSpaceDims.data(), 1, CUDA_C_64F, &state_)); + + // Query the size of the quantum state storage + std::size_t storageSize{0}; // only one storage component (tensor) is needed + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + handle_, state_, + 1, // only one storage component + &storageSize)); // storage size in bytes + const std::size_t stateVolume = + storageSize / sizeof(std::complex); // quantum state tensor volume + // (number of elements) + assert(stateVolume == gpuDataSize_); + // std::cout << "Quantum state storage size (bytes) = " << storageSize + // << std::endl; + + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + handle_, state_, + 1, // only one storage component (tensor) + std::vector({gpuData_}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state +} + +cudm_state cudm_state::zero_like(const cudm_state &other) { + cudm_state state; + state.handle_ = other.handle_; + state.hilbertSpaceDims_ = other.hilbertSpaceDims_; + state.gpuDataSize_ = other.gpuDataSize_; + const size_t dataSize = state.gpuDataSize_ * sizeof(std::complex); + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&state.gpuData_), dataSize)); + HANDLE_CUDA_ERROR(cudaMemset(state.gpuData_, 0, dataSize)); + + const size_t expectedDensityMatrixSize = + calculate_density_matrix_size(state.hilbertSpaceDims_); + const bool isDensityMat = expectedDensityMatrixSize == state.gpuDataSize_; + const cudensitymatStatePurity_t purity = isDensityMat + ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + HANDLE_CUDM_ERROR(cudensitymatCreateState( + state.handle_, purity, + static_cast(state.hilbertSpaceDims_.size()), + state.hilbertSpaceDims_.data(), 1, CUDA_C_64F, &state.state_)); + + // Query the size of the quantum state storage + std::size_t storageSize{0}; // only one storage component (tensor) is needed + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + state.handle_, state.state_, + 1, // only one storage component + &storageSize)); // storage size in bytes + const std::size_t stateVolume = + storageSize / sizeof(std::complex); // quantum state tensor volume + // (number of elements) + assert(stateVolume == state.gpuDataSize_); + // std::cout << "Quantum state storage size (bytes) = " << storageSize + // << std::endl; + + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + state.handle_, state.state_, + 1, // only one storage component (tensor) + std::vector({state.gpuData_}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state + return state; +} cudm_state::cudm_state(cudensitymatHandle_t handle, const std::vector> rawData, const std::vector &hilbertSpaceDims) - : rawData_(rawData), state_(nullptr), handle_(handle), - hilbertSpaceDims_(hilbertSpaceDims) { + : rawData_(rawData), gpuDataSize_(rawData.size()), state_(nullptr), + handle_(handle), hilbertSpaceDims_(hilbertSpaceDims) { if (rawData_.empty()) { throw std::invalid_argument("Raw data cannot be empty."); @@ -63,11 +156,12 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, } cudm_state::cudm_state(cudm_state &&other) noexcept - : rawData_(std::move(other.rawData_)), gpuData_(other.gpuData_), - state_(other.state_), handle_(other.handle_), + : rawData_(std::move(other.rawData_)), gpuDataSize_(other.gpuDataSize_), + gpuData_(other.gpuData_), state_(other.state_), handle_(other.handle_), hilbertSpaceDims_(std::move(other.hilbertSpaceDims_)) { other.gpuData_ = nullptr; other.state_ = nullptr; + other.gpuDataSize_ = 0; } cudm_state &cudm_state::operator=(cudm_state &&other) noexcept { @@ -83,6 +177,7 @@ cudm_state &cudm_state::operator=(cudm_state &&other) noexcept { // Move data from other rawData_ = std::move(other.rawData_); gpuData_ = other.gpuData_; + gpuDataSize_ = other.gpuDataSize_; state_ = other.state_; handle_ = other.handle_; hilbertSpaceDims_ = std::move(other.hilbertSpaceDims_); @@ -90,6 +185,7 @@ cudm_state &cudm_state::operator=(cudm_state &&other) noexcept { // Nullify other other.gpuData_ = nullptr; other.state_ = nullptr; + other.gpuDataSize_ = 0; } return *this; @@ -222,7 +318,7 @@ void cudm_state::dumpDeviceData() const { throw std::runtime_error("State is not initialized."); } - std::vector> hostBuffer(rawData_.size()); + std::vector> hostBuffer(gpuDataSize_); HANDLE_CUDA_ERROR(cudaMemcpy(hostBuffer.data(), get_device_pointer(), hostBuffer.size() * sizeof(std::complex), cudaMemcpyDefault)); @@ -308,13 +404,13 @@ void cudm_state::attach_storage() { } size_t cudm_state::calculate_state_vector_size( - const std::vector &hilbertSpaceDims) const { + const std::vector &hilbertSpaceDims) { return std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), 1, std::multiplies<>()); } size_t cudm_state::calculate_density_matrix_size( - const std::vector &hilbertSpaceDims) const { + const std::vector &hilbertSpaceDims) { size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); return vectorSize * vectorSize; } diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/cudaq/dynamics/cudm_time_stepper.cpp index 9c8360c78a..15be4af4f8 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/cudaq/dynamics/cudm_time_stepper.cpp @@ -45,10 +45,7 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, freeMem = static_cast(static_cast(freeMem) * 0.80); // Create a new state for the next step - std::vector> zero_initiailized_data( - state.get_raw_data().size(), {0.0, 0.0}); - cudm_state next_state(handle_, zero_initiailized_data, - state.get_hilbert_space_dims()); + cudm_state next_state = cudm_state::zero_like(state); if (!next_state.is_initialized()) { throw std::runtime_error("Next state failed to initialize."); diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp index 684d38d08f..7326d84a7f 100644 --- a/runtime/cudaq/dynamics/evolution.cpp +++ b/runtime/cudaq/dynamics/evolution.cpp @@ -125,16 +125,14 @@ evolve_result evolve_single( std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); - auto liouvillian = - helper.convert_to_cudensitymat_operator( - {}, hamiltonian, dims); + auto liouvillian = helper.construct_liouvillian( + hamiltonian, collapse_operators, dims, {}, false); + std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; // Need to pass liouvillian here auto time_stepper = std::make_shared(handle, liouvillian); - const std::vector> initialState = {{1.0, 0.0}, - {0.0, 0.0}}; auto integrator = std::make_unique( - cudm_state(handle, initialState, dims), 0.0, time_stepper, 1); - integrator->set_option("dt", 0.001); + cudm_state(handle, initial_state, dims), 0.0, time_stepper, 1); + integrator->set_option("dt", 0.0001); std::vector expectations; for (auto &obs : observables) @@ -144,7 +142,6 @@ evolve_result evolve_single( std::vector> expectationVals; for (const auto &step : schedule) { - std::cout << "Step: " << step << "\n"; integrator->integrate(step); auto [t, currentState] = integrator->get_state(); if (store_intermediate_results) { diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index 0a59a7a5d4..e3063a2e23 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -14,6 +14,8 @@ #include #include #include +#include "common/EigenDense.h" +#include using namespace cudaq; @@ -52,3 +54,33 @@ TEST_F(CuDensityExpectationTest, checkCompute) { EXPECT_NEAR(expVal.imag(), 0.0, 1e-12); } } + + +TEST_F(CuDensityExpectationTest, checkCompositeSystem) { + cudm_helper helper(handle_); + const std::vector dims = {2, 10}; + // Check number operator on boson Fock space + auto op = cudaq::matrix_operator::number(1); + auto cudmOp = helper.convert_to_cudensitymat_operator( + {}, op, dims); + + cudm_expectation expectation(handle_, cudmOp); + + for (std::size_t stateIdx = 0; stateIdx < dims[1]; ++stateIdx) { + Eigen::Vector2cd qubit_state; + qubit_state << 1.0, 0.0; + Eigen::VectorXcd cavity_state = Eigen::VectorXcd::Zero(dims[1]); + cavity_state[stateIdx] = 1.0; + Eigen::VectorXcd initial_state_vec = + Eigen::kroneckerProduct(cavity_state, qubit_state); + std::vector> initialState( + initial_state_vec.data(), + initial_state_vec.data() + initial_state_vec.size()); + auto inputState = std::make_unique(handle_, initialState, dims); + expectation.prepare(inputState->get_impl()); + const auto expVal = expectation.compute(inputState->get_impl(), 0.0); + std::cout << "Result: " << expVal << "\n"; + EXPECT_NEAR(expVal.real(), 1.0 * stateIdx, 1e-12); + EXPECT_NEAR(expVal.imag(), 0.0, 1e-12); + } +} \ No newline at end of file diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 60c8728956..d474067320 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -10,6 +10,8 @@ #include #include #include +#include "common/EigenDense.h" +#include TEST(EvolveTester, checkSimple) { const std::map dims = {{0, 2}}; @@ -27,8 +29,7 @@ TEST(EvolveTester, checkSimple) { }; cudaq::matrix_operator::define(op_id, {-1}, func); auto ham = cudaq::product_operator( - std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, - cudaq::matrix_operator(op_id, {0})); + 2.0 * M_PI * 0.1, cudaq::matrix_operator(op_id, {0})); constexpr int numSteps = 10; cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); @@ -65,3 +66,49 @@ TEST(EvolveTester, checkSimple) { EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); } } + +TEST(EvolveTester, checkCompositeSystem) { + constexpr int cavity_levels = 10; + const std::map dims = {{0, 2}, {1, cavity_levels}}; + auto a = cudaq::matrix_operator::annihilate(1); + auto a_dag = cudaq::matrix_operator::create(1); + + auto sm = cudaq::matrix_operator::annihilate(0); + auto sm_dag = cudaq::matrix_operator::create(0); + + auto atom_occ_op = cudaq::matrix_operator::number(0); + auto cavity_occ_op = cudaq::matrix_operator::number(1); + auto hamiltonian = 2 * M_PI * atom_occ_op + 2 * M_PI * cavity_occ_op + + 2 * M_PI * 0.25 * (sm * a_dag + sm_dag * a); + // auto matrix = hamiltonian.to_matrix(dims); + // std::cout << "Matrix:\n" << matrix.dump() << "\n"; + Eigen::Vector2cd qubit_state; + qubit_state << 1.0, 0.0; + Eigen::VectorXcd cavity_state = Eigen::VectorXcd::Zero(cavity_levels); + const int num_photons = 5; + cavity_state[num_photons] = 1.0; + Eigen::VectorXcd initial_state_vec = + Eigen::kroneckerProduct(cavity_state, qubit_state); + std::cout << "Initial state: \n" << initial_state_vec << "\n"; + constexpr int num_steps = 201; + cudaq::Schedule schedule(cudaq::linspace(0.0, 10, num_steps)); + auto initialState = cudaq::state::from_data( + std::make_pair(initial_state_vec.data(), initial_state_vec.size())); + + auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, + {}, {&cavity_occ_op, &atom_occ_op}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); + // std::vector theoryResults; + // for (const auto &t : schedule) { + // const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); + // theoryResults.emplace_back(expected); + // } + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 2); + std::cout << expVals[0] << "\n"; + // EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } +} From c134c57d267403fd13d2ffbdcadf91a1fb77e073 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 12 Feb 2025 00:30:26 -0800 Subject: [PATCH 246/311] Updating compute_lindblad_op with the updated append_elementary_operator_to_term signature Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 2 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 42 ++++++++----------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 2f807e8b6c..893dba8b32 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -80,7 +80,7 @@ class cudm_helper { void append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, const std::vector &elem_ops, - const std::vector> °rees); + const std::vector> °rees, bool is_dagger); // GPU memory management static void * diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index e992cf2971..68a74517ad 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -221,7 +221,7 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( void cudm_helper::append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, const std::vector &elem_ops, - const std::vector> °rees) { + const std::vector> °rees, bool is_dager) { if (degrees.empty()) { throw std::invalid_argument("Degrees vector cannot be empty."); @@ -248,21 +248,15 @@ void cudm_helper::append_elementary_operator_to_term( throw std::out_of_range("Degree cannot be negative!"); } allDegrees.emplace_back(degree); - allModeActionDuality.emplace_back(0); // left side + allModeActionDuality.emplace_back(is_dager ? 1 : 0); } } - // std::cout << "append_elementary_operator_to_term: " << elem_op << " -> " << - // term << ": degree = "; for (int degree : degrees) { - // std::cout << degree << " "; - // } - // std::cout << "\n"; - assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle, term, static_cast(allDegrees.size()), elem_ops.data(), - allDegrees.data(), allModeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), - {nullptr, nullptr})); + allDegrees.data(), allModeActionDuality.data(), + make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); } // Function to create and append a scalar to a term @@ -310,6 +304,7 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( std::vector terms; std::vector elem_ops; + std::vector> all_degrees; try { for (const auto &c_op : c_ops) { @@ -352,8 +347,10 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( "compute_lindblad_operator."); } - elem_ops.push_back(L_elem_op); - elem_ops.push_back(L_dagger_elem_op); + elem_ops.emplace_back(L_elem_op); + all_degrees.emplace_back(L_op.degrees); + elem_ops.emplace_back(L_dagger_elem_op); + all_degrees.emplace_back(L_dagger_op.degrees); // D1 = L * Lt cudensitymatOperatorTerm_t term_D1; @@ -361,9 +358,7 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D1)); - append_elementary_operator_to_term(term_D1, {L_elem_op}, {{0}}); - - append_elementary_operator_to_term(term_D1, {L_dagger_elem_op}, {{0}}); + append_elementary_operator_to_term(term_D1, elem_ops, all_degrees, false); // Add term D1 to the Lindblad operator @@ -378,9 +373,7 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D2)); - append_elementary_operator_to_term(term_D2, {L_dagger_elem_op}, {{0}}); - - append_elementary_operator_to_term(term_D2, {L_elem_op}, {{0}}); + append_elementary_operator_to_term(term_D2, elem_ops, all_degrees, false); // Add term D2 to the Lindblad operator HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( @@ -392,8 +385,7 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D3)); - append_elementary_operator_to_term(term_D3, {L_elem_op}, {{0}}); - append_elementary_operator_to_term(term_D3, {L_dagger_elem_op}, {{0}}); + append_elementary_operator_to_term(term_D3, elem_ops, all_degrees, true); // Add term D3 to the Lindblad operator HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( @@ -459,10 +451,6 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( for (const auto &product_op : op.get_terms()) { cudensitymatOperatorTerm_t term; - // std::cout << "mode_extent: "; - // for (const auto& mode_extent: mode_extents) - // std::cout << mode_extent << " "; - // std::cout << "\n"; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); @@ -483,16 +471,12 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( throw std::runtime_error("Unhandled type!"); } } - append_elementary_operator_to_term(term, elem_ops, all_degrees); - // Handle the coefficient - // Static value without parameter: as it is - // Static value with parameter: Callback + append_elementary_operator_to_term(term, elem_ops, all_degrees, false); auto coeff = product_op.get_coefficient(); cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (!coeff.get_generator()) { const auto coeffVal = coeff.evaluate(); - // std::cout << "Append product term " << term << " with coeff = " << coeffVal << " to operator " << operator_handle << "\n"; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle, operator_handle, term, 0, make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), From 1e693dcafdf41da5e7715e4ebd7686e26cfa4e63 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 13 Feb 2025 03:39:08 +0000 Subject: [PATCH 247/311] Code restructure: separate cudm-dependency into nvqir target; no operators dependency on CUDA/cuquantum Signed-off-by: Thien Nguyen --- runtime/cudaq/base_integrator.h | 100 +++------- runtime/cudaq/dynamics/CMakeLists.txt | 14 -- runtime/cudaq/dynamics/evolution.cpp | 175 ------------------ runtime/cudaq/dynamics/helpers.cpp | 1 - .../cudaq/dynamics/runge_kutta_integrator.cpp | 86 --------- runtime/cudaq/evolution.h | 2 +- runtime/cudaq/runge_kutta_integrator.h | 48 ++--- runtime/nvqir/cudensitymat/CMakeLists.txt | 13 +- .../cudensitymat}/cudm_error_handling.h | 0 runtime/nvqir/cudensitymat/cudm_evolution.cpp | 90 +++++++++ .../cudensitymat}/cudm_expectation.cpp | 6 +- .../cudensitymat}/cudm_expectation.h | 0 .../cudensitymat}/cudm_helpers.cpp | 4 +- .../cudensitymat}/cudm_helpers.h | 0 .../cudensitymat}/cudm_op_conversion.cpp | 4 +- .../cudensitymat}/cudm_op_conversion.h | 2 +- .../cudensitymat}/cudm_solver.cpp | 8 +- .../cudensitymat}/cudm_solver.h | 4 +- .../cudensitymat}/cudm_state.cpp | 62 ++++++- .../cudensitymat}/cudm_state.h | 7 +- .../cudensitymat}/cudm_time_stepper.cpp | 6 +- .../cudensitymat}/cudm_time_stepper.h | 2 +- .../cudensitymat/runge_kutta_integrator.cpp | 113 +++++++++++ unittests/CMakeLists.txt | 1 + unittests/dynamics/test_cudm_expectation.cpp | 8 +- unittests/dynamics/test_cudm_helpers.cpp | 6 +- unittests/dynamics/test_cudm_state.cpp | 6 +- unittests/dynamics/test_cudm_time_stepper.cpp | 8 +- unittests/dynamics/test_evolve_single.cpp | 33 ++-- unittests/dynamics/test_mocks.h | 2 +- .../dynamics/test_runge_kutta_integrator.cpp | 95 +++++----- 31 files changed, 422 insertions(+), 484 deletions(-) delete mode 100644 runtime/cudaq/dynamics/evolution.cpp delete mode 100644 runtime/cudaq/dynamics/runge_kutta_integrator.cpp rename runtime/{cudaq => nvqir/cudensitymat}/cudm_error_handling.h (100%) create mode 100644 runtime/nvqir/cudensitymat/cudm_evolution.cpp rename runtime/{cudaq/dynamics => nvqir/cudensitymat}/cudm_expectation.cpp (96%) rename runtime/{cudaq => nvqir/cudensitymat}/cudm_expectation.h (100%) rename runtime/{cudaq/dynamics => nvqir/cudensitymat}/cudm_helpers.cpp (99%) rename runtime/{cudaq => nvqir/cudensitymat}/cudm_helpers.h (100%) rename runtime/{cudaq/dynamics => nvqir/cudensitymat}/cudm_op_conversion.cpp (99%) rename runtime/{cudaq => nvqir/cudensitymat}/cudm_op_conversion.h (99%) rename runtime/{cudaq/dynamics => nvqir/cudensitymat}/cudm_solver.cpp (94%) rename runtime/{cudaq => nvqir/cudensitymat}/cudm_solver.h (97%) rename runtime/{cudaq/dynamics => nvqir/cudensitymat}/cudm_state.cpp (86%) rename runtime/{cudaq => nvqir/cudensitymat}/cudm_state.h (97%) rename runtime/{cudaq/dynamics => nvqir/cudensitymat}/cudm_time_stepper.cpp (97%) rename runtime/{cudaq => nvqir/cudensitymat}/cudm_time_stepper.h (97%) create mode 100644 runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index fed0238ea5..9838e1a800 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -6,76 +6,30 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#pragma once - -#include "base_time_stepper.h" -#include "operators.h" -#include "schedule.h" -#include -#include -#include - -namespace cudaq { -template -class BaseIntegrator { -protected: - std::map integrator_options; - TState state; - double t; - // std::map dimensions; - // std::shared_ptr schedule; - // std::shared_ptr> hamiltonian; - std::shared_ptr> stepper; - // std::vector>> collapse_operators; - - virtual void post_init() = 0; - -public: - /// @brief Default constructor - BaseIntegrator() = default; - - /// @brief Constructor to initialize the integrator with a state and time - /// stepper. - /// @param initial_state Initial quantum state. - /// @param t0 Initial time. - /// @param stepper Time stepper instance. - BaseIntegrator(TState &&initial_state, double t0, - std::shared_ptr> stepper) - : state(std::move(initial_state)), t(t0), stepper(std::move(stepper)) { - if (!this->stepper) { - throw std::runtime_error("Time stepper is not initialized."); - } - } - - virtual ~BaseIntegrator() = default; - - /// @brief Set the initial state and time - void set_state(const TState &initial_state, double t0 = 0.0) { - state = initial_state; - t = t0; - } - - /// @brief Set an option for the integrator - void set_option(const std::string &key, double value) { - integrator_options[key] = value; - } - - /// @brief Set the system parameters (dimensions, schedule, and operators) - // void set_system(const std::map &dimensions, - // std::shared_ptr schedule, - // std::shared_ptr> hamiltonian, - // std::vector>> - // collapse_operators = {}) { - // this->dimensions = dimensions; - // this->schedule = schedule; - // this->hamiltonian = hamiltonian; - // this->collapse_operators = collapse_operators; - // } - - /// @brief Perform integration to the target time. - virtual void integrate(double target_time) = 0; - - /// @brief Get the current time and state. - std::pair get_state() const { return {t, state}; } -}; -} // namespace cudaq + #pragma once + + #include "operators.h" + #include "schedule.h" + #include + #include + #include + + namespace cudaq { + class BaseIntegrator { + public: + /// @brief Default constructor + BaseIntegrator() = default; + + virtual ~BaseIntegrator() = default; + + /// @brief Set the initial state and time + virtual void set_state(cudaq::state initial_state, double t0) = 0; + + /// @brief Perform integration to the target time. + virtual void integrate(double target_time) = 0; + + /// @brief Get the current time and state. + virtual std::pair get_state() = 0; + }; + } // namespace cudaq + \ No newline at end of file diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 5eefd2029c..7b15e53633 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -13,10 +13,6 @@ set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC helpers.cpp rydberg_hamiltonian.cpp - cudm_helpers.cpp - cudm_state.cpp - cudm_time_stepper.cpp - runge_kutta_integrator.cpp definition.cpp scalar_operators.cpp matrix_operators.cpp @@ -26,15 +22,8 @@ set(CUDAQ_OPS_SRC manipulation.cpp helpers.cpp rydberg_hamiltonian.cpp - cudm_expectation.cpp - evolution.cpp ) -set(CUQUANTUM_INSTALL_PREFIX "/usr/local/lib/python3.10/dist-packages/cuquantum") -if (NOT DEFINED CUQUANTUM_INSTALL_PREFIX) - message(FATAL_ERROR "CUQUANTUM_INSTALL_PREFIX is not defined.") -endif() - add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) set_property(GLOBAL APPEND PROPERTY CUDAQ_RUNTIME_LIBS ${LIBRARY_NAME}) target_compile_definitions(${LIBRARY_NAME} PRIVATE -DCUDAQ_INSTANTIATE_TEMPLATES) @@ -44,12 +33,9 @@ target_include_directories(${LIBRARY_NAME} $ $ $ - /usr/local/cuda/targets/x86_64-linux/include $ PRIVATE .) -target_link_libraries(${LIBRARY_NAME} PRIVATE ${CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0) - set (OPERATOR_DEPENDENCIES "") list(APPEND OPERATOR_DEPENDENCIES fmt::fmt-header-only) add_openmp_configurations(${LIBRARY_NAME} OPERATOR_DEPENDENCIES) diff --git a/runtime/cudaq/dynamics/evolution.cpp b/runtime/cudaq/dynamics/evolution.cpp deleted file mode 100644 index 7326d84a7f..0000000000 --- a/runtime/cudaq/dynamics/evolution.cpp +++ /dev/null @@ -1,175 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/evolution.h" -#include "cudaq/runge_kutta_integrator.h" -#include - -#include -#include -#include -#include - -namespace cudaq { - -// Can be removed -// matrix_2 taylor_series_expm(const matrix_2 &op_matrix, int order = 20) { -// matrix_2 result = matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); -// matrix_2 op_matrix_n = -// matrix_2(op_matrix.get_rows(), op_matrix.get_columns()); - -// for (size_t i = 0; i < op_matrix.get_rows(); i++) { -// result[{i, i}] = std::complex(1.0, 0.0); -// op_matrix_n[{i, i}] = std::complex(1.0, 0.0); -// } - -// double factorial = 1.0; -// for (int n = 1; n <= order; n++) { -// op_matrix_n *= op_matrix; -// factorial *= n; -// result += std::complex(1.0 / factorial, 0.0) * op_matrix_n; -// } - -// return result; -// } - -// matrix_2 compute_step_matrix( -// const operator_sum &hamiltonian, const std::map &dimensions, -// const std::map> ¶meters, double dt, -// bool use_gpu) { -// matrix_2 op_matrix = hamiltonian.to_matrix(dimensions, parameters); -// op_matrix = dt * std::complex(0, -1) * op_matrix; - -// if (use_gpu) { -// // TODO: Implement GPU matrix exponential using CuPy or cuQuantum -// throw std::runtime_error( -// "GPU-based matrix exponentiation not implemented."); -// } else { -// return taylor_series_expm(op_matrix); -// } -// } - -// void add_noise_channel_for_step( -// const std::string &step_kernel_name, cudaq::noise_model &noise_model, -// const std::vector &collapse_operators, -// const std::map &dimensions, -// const std::map> ¶meters, double dt) -// { -// for (const auto &collapse_op : collapse_operators) { -// matrix_2 L = collapse_op.to_matrix(dimensions, parameters); -// matrix_2 G = std::complex(-0.5, 0.0) * (L * L); - -// // Kraus operators -// matrix_2 M0 = (dt * G) + matrix_2(L.get_rows(), L.get_columns()); -// matrix_2 M1 = std::sqrt(dt) * L; - -// try { -// noise_model.add_all_qubit_channel( -// step_kernel_name, kraus_channel({std::move(M0), std::move(M1)})); -// } catch (const std::exception &e) { -// std::cerr << "Error adding noise channel: " << e.what() << std::endl; -// throw; -// } -// } -// } - -// evolve_result launch_analog_hamiltonian_kernel(const std::string -// &target_name, -// const rydberg_hamiltonian &hamiltonian, -// const Schedule &schedule, -// int shots_count, bool is_async = false) { -// // Generate the time series -// std::vector> amp_ts, ph_ts, dg_ts; - -// auto current_schedule = schedule; -// current_schedule.reset(); - -// while(auto t = current_schedule.current_step()) { -// std::map> parameters = {{"time", -// t.value()}}; - -// amp_ts.emplace_back(hamiltonian.get_amplitude().evaluate(parameters).real(), -// t.value().real()); -// ph_ts.emplace_back(hamiltonian.get_phase().evaluate(parameters).real(), -// t.value().real()); -// dg_ts.emplace_back(hamiltonian.get_delta_global().evaluate(parameters).real(), -// t.value().real()); - -// ++schedule; -// } - -// // Atom arrangement and physical fields -// cudaq::ahs::AtomArrangement atoms; - -// } -evolve_result evolve_single( - const operator_sum &hamiltonian, - const std::map &dimensions, const Schedule &schedule, - const state &initial_state, - const std::vector *> - &collapse_operators, - const std::vector *> &observables, - bool store_intermediate_results, - std::shared_ptr> inputIntegrator, - std::optional shots_count) { - cudensitymatHandle_t handle; - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); - - cudm_helper helper(handle); - - std::vector dims; - for (const auto &[id, dim] : dimensions) - dims.emplace_back(dim); - auto liouvillian = helper.construct_liouvillian( - hamiltonian, collapse_operators, dims, {}, false); - std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; - // Need to pass liouvillian here - auto time_stepper = std::make_shared(handle, liouvillian); - auto integrator = std::make_unique( - cudm_state(handle, initial_state, dims), 0.0, time_stepper, 1); - integrator->set_option("dt", 0.0001); - - std::vector expectations; - for (auto &obs : observables) - expectations.emplace_back(cudm_expectation( - handle, helper.convert_to_cudensitymat_operator( - {}, *obs, dims))); - - std::vector> expectationVals; - for (const auto &step : schedule) { - integrator->integrate(step); - auto [t, currentState] = integrator->get_state(); - if (store_intermediate_results) { - std::vector expVals; - for (auto &expectation : expectations) { - expectation.prepare(currentState.get_impl()); - const auto expVal = expectation.compute(currentState.get_impl(), step); - expVals.emplace_back(expVal.real()); - } - expectationVals.emplace_back(std::move(expVals)); - } - } - - if (store_intermediate_results) { - // TODO: need to convert to proper state - return evolve_result({initial_state}, expectationVals); - } else { - // Only final state is needed - auto [finalTime, finalState] = integrator->get_state(); - std::vector expVals; - for (auto &expectation : expectations) { - expectation.prepare(finalState.get_impl()); - const auto expVal = expectation.compute(finalState.get_impl(), finalTime); - expVals.emplace_back(expVal.real()); - } - // TODO: need to convert to proper state - return evolve_result(initial_state, expVals); - } -} - -} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 47301ad35f..2b5c3413c4 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -7,7 +7,6 @@ ******************************************************************************/ #include "cudaq/helpers.h" -#include "cudaq/cudm_error_handling.h" #include "cudaq/operators.h" #include #include diff --git a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp b/runtime/cudaq/dynamics/runge_kutta_integrator.cpp deleted file mode 100644 index 4217edb48b..0000000000 --- a/runtime/cudaq/dynamics/runge_kutta_integrator.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/runge_kutta_integrator.h" -#include - -using namespace cudaq; - -namespace cudaq { -void runge_kutta_integrator::integrate(double target_time) { - if (!this->stepper) { - throw std::runtime_error("Time stepper is not initialized."); - } - - if (this->integrator_options.find("dt") == this->integrator_options.end()) { - throw std::invalid_argument( - "Time step size (dt) is missing from integrator options."); - } - - double dt = this->integrator_options["dt"]; - if (dt <= 0) { - throw std::invalid_argument("Invalid time step size for integration."); - } - - while (this->t < target_time) { - double step_size = std::min(dt, target_time - this->t); - - // std::cout << "Runge-Kutta step at time " << this->t - // << " with step size: " << step_size << std::endl; - - if (this->substeps_ == 1) { - // Euler method (1st order) - cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); - k1 *= step_size; - this->state += k1; - } else if (this->substeps_ == 2) { - // Midpoint method (2nd order) - cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); - k1 *= (step_size / 2.0); - - this->state += k1; - - cudm_state k2 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k2 *= (step_size / 2.0); - - this->state += k2; - } else if (this->substeps_ == 4) { - // Runge-Kutta method (4th order) - cudm_state k1 = this->stepper->compute(this->state, this->t, step_size); - k1 *= step_size; - - this->state += k1 * 0.5; - - cudm_state k2 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k2 *= step_size; - - this->state += k2 * 0.5; - - cudm_state k3 = this->stepper->compute( - this->state, this->t + step_size / 2.0, step_size); - k3 *= step_size; - - this->state += k3; - - cudm_state k4 = - this->stepper->compute(this->state, this->t + step_size, step_size); - k4 *= step_size; - - this->state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); - } - - // Update time - this->t += step_size; - } - - std::cout << "Integration complete. Final time: " << this->t << std::endl; -} - -} // namespace cudaq diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index 87b315de7d..f071c4726d 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -25,11 +25,11 @@ evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, const state &initial_state, + BaseIntegrator& integrator, const std::vector *> &collapse_operators = {}, const std::vector *> &observables = {}, bool store_intermediate_results = false, - std::shared_ptr> integrator = nullptr, std::optional shots_count = std::nullopt); // class Evolution { // public: diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index bd6b4445e3..ed52612f97 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -9,44 +9,32 @@ #pragma once #include "cudaq/base_integrator.h" -#include "cudaq/cudm_state.h" -#include "cudaq/cudm_time_stepper.h" -#include #include namespace cudaq { -class runge_kutta_integrator : public BaseIntegrator { +class cudm_state; +class cudm_time_stepper; +class runge_kutta_integrator : public BaseIntegrator { + +public: + std::optional order; + std::optional dt; + public: - /// @brief Constructor to initialize the Runge-Kutta integrator - /// @param initial_state Initial quantum state. - /// @param t0 Initial time. - /// @param stepper Time stepper instance. - /// @param substeps Number of Runge-Kutta substeps (must be 1, 2, or 4) + runge_kutta_integrator() = default; runge_kutta_integrator(cudm_state &&initial_state, double t0, std::shared_ptr stepper, - int substeps = 4) - : BaseIntegrator(std::move(initial_state), t0, stepper), - substeps_(substeps) { - if (!stepper) { - throw std::invalid_argument("Time stepper must be initialized."); - } + int substeps = 4); - if (substeps_ != 1 && substeps_ != 2 && substeps_ != 4) { - throw std::invalid_argument("Runge-Kutta substeps must be 1, 2, or 4."); - } - this->post_init(); - } - - /// @brief Perform Runge-Kutta integration until the target time. - /// @param target_time The final time to integrate to. void integrate(double target_time) override; - -protected: - /// @brief Any post-initialization setup - void post_init() override {} - + void set_state(cudaq::state initial_state, double t0) override; + void set_state(cudm_state &&initial_state); + void set_stepper(std::shared_ptr stepper); + std::pair get_state() override; + std::pair get_cudm_state(); private: - // Number of substeps in RK integration (1, 2, or 4) - int substeps_; + std::unique_ptr m_state; + double m_t; + std::shared_ptr m_stepper; }; } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 20611b2d6a..3192e41a96 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -23,10 +23,19 @@ find_file(CUDENSITYMAT_INC ) message(STATUS "cudensitymat header: ${CUDENSITYMAT_INC}") -get_filename_component(CUDENSITYMAT_INCLUDE_DIR ${CUDENSITYMAT_INC} DIRECTORY) -add_library(${LIBRARY_NAME} SHARED CuDensityMatSim.cpp mpi_support.cpp) +add_library(${LIBRARY_NAME} SHARED + CuDensityMatSim.cpp + mpi_support.cpp + cudm_helpers.cpp + cudm_state.cpp + cudm_time_stepper.cpp + runge_kutta_integrator.cpp + cudm_expectation.cpp + cudm_evolution.cpp +) +message("CUDAToolkit_INCLUDE_DIRS = ${CUDAToolkit_INCLUDE_DIRS}") target_include_directories(${LIBRARY_NAME} PRIVATE . .. diff --git a/runtime/cudaq/cudm_error_handling.h b/runtime/nvqir/cudensitymat/cudm_error_handling.h similarity index 100% rename from runtime/cudaq/cudm_error_handling.h rename to runtime/nvqir/cudensitymat/cudm_error_handling.h diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp new file mode 100644 index 0000000000..48114b22c7 --- /dev/null +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -0,0 +1,90 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/evolution.h" +#include "cudm_error_handling.h" +#include "cudaq/runge_kutta_integrator.h" +#include +#include "cudm_expectation.h" +#include "cudm_time_stepper.h" +#include +#include +#include +#include "cudm_helpers.h" +#include "cudm_state.h" +namespace cudaq { +evolve_result evolve_single( + const operator_sum &hamiltonian, + const std::map &dimensions, const Schedule &schedule, + const state &initial_state, + BaseIntegrator& in_integrator, + const std::vector *> + &collapse_operators, + const std::vector *> &observables, + bool store_intermediate_results, + std::optional shots_count) { + cudensitymatHandle_t handle; + HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + + cudm_helper helper(handle); + + std::vector dims; + for (const auto &[id, dim] : dimensions) + dims.emplace_back(dim); + auto liouvillian = helper.construct_liouvillian( + hamiltonian, collapse_operators, dims, {}, false); + std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; + // Need to pass liouvillian here + auto time_stepper = std::make_shared(handle, liouvillian); + runge_kutta_integrator &integrator = + dynamic_cast(in_integrator); + integrator.set_stepper(time_stepper); + integrator.set_state(cudm_state(handle, initial_state, dims)); + // auto integrator = std::make_unique( + // cudm_state(handle, initial_state, dims), 0.0, time_stepper, 1); + // integrator.set_option("dt", 0.000001); + + std::vector expectations; + for (auto &obs : observables) + expectations.emplace_back(cudm_expectation( + handle, helper.convert_to_cudensitymat_operator( + {}, *obs, dims))); + + std::vector> expectationVals; + for (const auto &step : schedule) { + integrator.integrate(step); + auto [t, currentState] = integrator.get_cudm_state(); + if (store_intermediate_results) { + std::vector expVals; + for (auto &expectation : expectations) { + expectation.prepare(currentState->get_impl()); + const auto expVal = expectation.compute(currentState->get_impl(), step); + expVals.emplace_back(expVal.real()); + } + expectationVals.emplace_back(std::move(expVals)); + } + } + + if (store_intermediate_results) { + // TODO: need to convert to proper state + return evolve_result({initial_state}, expectationVals); + } else { + // Only final state is needed + auto [finalTime, finalState] = integrator.get_cudm_state(); + std::vector expVals; + for (auto &expectation : expectations) { + expectation.prepare(finalState->get_impl()); + const auto expVal = expectation.compute(finalState->get_impl(), finalTime); + expVals.emplace_back(expVal.real()); + } + // TODO: need to convert to proper state + return evolve_result(initial_state, expVals); + } +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/cudm_expectation.cpp b/runtime/nvqir/cudensitymat/cudm_expectation.cpp similarity index 96% rename from runtime/cudaq/dynamics/cudm_expectation.cpp rename to runtime/nvqir/cudensitymat/cudm_expectation.cpp index 2705b648b4..46a120a75a 100644 --- a/runtime/cudaq/dynamics/cudm_expectation.cpp +++ b/runtime/nvqir/cudensitymat/cudm_expectation.cpp @@ -6,10 +6,10 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/cudm_expectation.h" +#include "cudm_expectation.h" #include "common/Logger.h" -#include "cudaq/cudm_error_handling.h" -#include "cudaq/cudm_helpers.h" +#include "cudm_error_handling.h" +#include "cudm_helpers.h" #include namespace cudaq { diff --git a/runtime/cudaq/cudm_expectation.h b/runtime/nvqir/cudensitymat/cudm_expectation.h similarity index 100% rename from runtime/cudaq/cudm_expectation.h rename to runtime/nvqir/cudensitymat/cudm_expectation.h diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp similarity index 99% rename from runtime/cudaq/dynamics/cudm_helpers.cpp rename to runtime/nvqir/cudensitymat/cudm_helpers.cpp index 68a74517ad..5864f3718c 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/cudm_helpers.h" -#include "cudaq/cudm_error_handling.h" +#include "cudm_helpers.h" +#include "cudm_error_handling.h" using namespace cudaq; diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h similarity index 100% rename from runtime/cudaq/cudm_helpers.h rename to runtime/nvqir/cudensitymat/cudm_helpers.h diff --git a/runtime/cudaq/dynamics/cudm_op_conversion.cpp b/runtime/nvqir/cudensitymat/cudm_op_conversion.cpp similarity index 99% rename from runtime/cudaq/dynamics/cudm_op_conversion.cpp rename to runtime/nvqir/cudensitymat/cudm_op_conversion.cpp index 2b45503cfa..126419be18 100644 --- a/runtime/cudaq/dynamics/cudm_op_conversion.cpp +++ b/runtime/nvqir/cudensitymat/cudm_op_conversion.cpp @@ -6,8 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/cudm_op_conversion.h" -#include "cudaq/cudm_error_handling.h" +#include "cudm_op_conversion.h" +#include "cudm_error_handling.h" #include #include #include diff --git a/runtime/cudaq/cudm_op_conversion.h b/runtime/nvqir/cudensitymat/cudm_op_conversion.h similarity index 99% rename from runtime/cudaq/cudm_op_conversion.h rename to runtime/nvqir/cudensitymat/cudm_op_conversion.h index ed8db413b0..c13de93706 100644 --- a/runtime/cudaq/cudm_op_conversion.h +++ b/runtime/nvqir/cudensitymat/cudm_op_conversion.h @@ -8,7 +8,7 @@ #pragma once -#include "cudaq/cudm_helpers.h" +#include "cudm_helpers.h" #include "cudaq/operators.h" #include "cudaq/schedule.h" #include diff --git a/runtime/cudaq/dynamics/cudm_solver.cpp b/runtime/nvqir/cudensitymat/cudm_solver.cpp similarity index 94% rename from runtime/cudaq/dynamics/cudm_solver.cpp rename to runtime/nvqir/cudensitymat/cudm_solver.cpp index 38f9a23591..2da0ac54a6 100644 --- a/runtime/cudaq/dynamics/cudm_solver.cpp +++ b/runtime/nvqir/cudensitymat/cudm_solver.cpp @@ -6,10 +6,10 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/cudm_solver.h" -#include "cudaq/cudm_helpers.h" -#include "cudaq/cudm_state.h" -#include "cudaq/cudm_time_stepper.h" +#include "cudm_solver.h" +#include "cudm_helpers.h" +#include "cudm_state.h" +#include "cudm_time_stepper.h" namespace cudaq { cudm_solver::cudm_solver(const Config &config) : config_(config) { diff --git a/runtime/cudaq/cudm_solver.h b/runtime/nvqir/cudensitymat/cudm_solver.h similarity index 97% rename from runtime/cudaq/cudm_solver.h rename to runtime/nvqir/cudensitymat/cudm_solver.h index 32fc7b00a8..80b01b669a 100644 --- a/runtime/cudaq/cudm_solver.h +++ b/runtime/nvqir/cudensitymat/cudm_solver.h @@ -11,8 +11,8 @@ #include "runtime/common/EvolveResult.h" #include #include -#include -#include +#include "cudm_helpers.h> +#include "cudm_state.h> #include #include #include diff --git a/runtime/cudaq/dynamics/cudm_state.cpp b/runtime/nvqir/cudensitymat/cudm_state.cpp similarity index 86% rename from runtime/cudaq/dynamics/cudm_state.cpp rename to runtime/nvqir/cudensitymat/cudm_state.cpp index f52eefcfbb..ac13c96998 100644 --- a/runtime/cudaq/dynamics/cudm_state.cpp +++ b/runtime/nvqir/cudensitymat/cudm_state.cpp @@ -7,12 +7,14 @@ ******************************************************************************/ #include -#include +#include "cudm_state.h" +#include "cudm_error_handling.h" #include #include #include #include #include +#include #include namespace cudaq { @@ -109,6 +111,52 @@ cudm_state cudm_state::zero_like(const cudm_state &other) { return state; } +cudm_state cudm_state::clone(const cudm_state &other) { + cudm_state state; + state.handle_ = other.handle_; + state.hilbertSpaceDims_ = other.hilbertSpaceDims_; + state.gpuDataSize_ = other.gpuDataSize_; + const size_t dataSize = state.gpuDataSize_ * sizeof(std::complex); + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&state.gpuData_), dataSize)); + HANDLE_CUDA_ERROR( + cudaMemcpy(state.gpuData_, other.gpuData_, dataSize, cudaMemcpyDefault)); + + const size_t expectedDensityMatrixSize = + calculate_density_matrix_size(state.hilbertSpaceDims_); + const bool isDensityMat = expectedDensityMatrixSize == state.gpuDataSize_; + const cudensitymatStatePurity_t purity = isDensityMat + ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + HANDLE_CUDM_ERROR(cudensitymatCreateState( + state.handle_, purity, + static_cast(state.hilbertSpaceDims_.size()), + state.hilbertSpaceDims_.data(), 1, CUDA_C_64F, &state.state_)); + + // Query the size of the quantum state storage + std::size_t storageSize{0}; // only one storage component (tensor) is needed + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + state.handle_, state.state_, + 1, // only one storage component + &storageSize)); // storage size in bytes + const std::size_t stateVolume = + storageSize / sizeof(std::complex); // quantum state tensor volume + // (number of elements) + assert(stateVolume == state.gpuDataSize_); + // std::cout << "Quantum state storage size (bytes) = " << storageSize + // << std::endl; + + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + state.handle_, state.state_, + 1, // only one storage component (tensor) + std::vector({state.gpuData_}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state + return state; +} + cudm_state::cudm_state(cudensitymatHandle_t handle, const std::vector> rawData, const std::vector &hilbertSpaceDims) @@ -225,11 +273,11 @@ std::vector cudm_state::get_hilbert_space_dims() const { cudensitymatHandle_t cudm_state::get_handle() const { return handle_; } cudm_state cudm_state::operator+(const cudm_state &other) const { - if (rawData_.size() != other.rawData_.size()) { + if (gpuDataSize_ != other.gpuDataSize_) { throw std::invalid_argument("State size mismatch for addition."); } - cudm_state result = cudm_state(handle_, rawData_, hilbertSpaceDims_); + cudm_state result = cudm_state::clone(*this); double scalingFactor = 1.0; double *gpuScalingFactor; @@ -246,8 +294,10 @@ cudm_state cudm_state::operator+(const cudm_state &other) const { } cudm_state &cudm_state::operator+=(const cudm_state &other) { - if (rawData_.size() != other.rawData_.size()) { - throw std::invalid_argument("State size mismatch for addition."); + if (gpuDataSize_ != other.gpuDataSize_) { + throw std::invalid_argument( + fmt::format("State size mismatch for addition ({} vs {}).", + gpuDataSize_, other.gpuDataSize_)); } double scalingFactor = 1.0; @@ -286,7 +336,7 @@ cudm_state cudm_state::operator*(double scalar) const { sizeof(std::complex), cudaMemcpyHostToDevice)); - cudm_state result(handle_, rawData_, hilbertSpaceDims_); + cudm_state result = cudm_state::clone(*this); HANDLE_CUDM_ERROR( cudensitymatStateComputeScaling(handle_, result.state_, gpuScalar, 0)); diff --git a/runtime/cudaq/cudm_state.h b/runtime/nvqir/cudensitymat/cudm_state.h similarity index 97% rename from runtime/cudaq/cudm_state.h rename to runtime/nvqir/cudensitymat/cudm_state.h index c72494cf62..05473e581b 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/nvqir/cudensitymat/cudm_state.h @@ -9,11 +9,12 @@ #pragma once #include -#include -#include +#include "cudm_error_handling.h" +#include "cudm_helpers.h" #include #include #include +#include namespace cudaq { // Enum to specify the initial quantum state. @@ -34,7 +35,7 @@ class cudm_state { // @brief Create a zero state static cudm_state zero_like(const cudm_state &other); - + static cudm_state clone(const cudm_state &other); // Prevent copies (avoids double free issues) cudm_state(const cudm_state &) = delete; cudm_state &operator=(const cudm_state &) = delete; diff --git a/runtime/cudaq/dynamics/cudm_time_stepper.cpp b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp similarity index 97% rename from runtime/cudaq/dynamics/cudm_time_stepper.cpp rename to runtime/nvqir/cudensitymat/cudm_time_stepper.cpp index 15be4af4f8..c8f3e2070a 100644 --- a/runtime/cudaq/dynamics/cudm_time_stepper.cpp +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp @@ -6,9 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/cudm_time_stepper.h" -#include "cudaq/cudm_error_handling.h" -#include "cudaq/cudm_helpers.h" +#include "cudm_time_stepper.h" +#include "cudm_error_handling.h" +#include "cudm_helpers.h" #include namespace cudaq { diff --git a/runtime/cudaq/cudm_time_stepper.h b/runtime/nvqir/cudensitymat/cudm_time_stepper.h similarity index 97% rename from runtime/cudaq/cudm_time_stepper.h rename to runtime/nvqir/cudensitymat/cudm_time_stepper.h index 6037ee00f1..44acac71a7 100644 --- a/runtime/cudaq/cudm_time_stepper.h +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.h @@ -9,7 +9,7 @@ #pragma once #include "cudaq/base_time_stepper.h" -#include "cudaq/cudm_state.h" +#include "cudm_state.h" #include namespace cudaq { diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp new file mode 100644 index 0000000000..133a956dc1 --- /dev/null +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/runge_kutta_integrator.h" +#include "cudm_state.h" +#include "cudm_time_stepper.h" +namespace cudaq { +void runge_kutta_integrator::set_state(cudaq::state initial_state, double t0) { + // TODO +} +std::pair runge_kutta_integrator::get_state() { + // TODO: + return std::make_pair(0.0, cudaq::state(nullptr)); +} + +// FIXME: remove this +std::pair runge_kutta_integrator::get_cudm_state() { + return std::make_pair(m_t, m_state.get()); +} + +void runge_kutta_integrator::integrate(double target_time) { + if (!m_stepper) { + throw std::runtime_error("Time stepper is not initialized."); + } + + if (dt.has_value() && dt.value() <= 0.0) { + throw std::invalid_argument("Invalid time step size for integration."); + } + + if (!m_state) { + throw std::runtime_error("Initial state has not been set."); + } + const auto substeps = order.value_or(4); + while (m_t < target_time) { + double step_size = + std::min(dt.value_or(target_time - m_t), target_time - m_t); + + // std::cout << "Runge-Kutta step at time " << m_t + // << " with step size: " << step_size << std::endl; + + if (substeps == 1) { + // Euler method (1st order) + cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); + k1 *= step_size; + *m_state += k1; + } else if (substeps == 2) { + // Midpoint method (2nd order) + cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); + k1 *= (step_size / 2.0); + + *m_state += k1; + + cudm_state k2 = + m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); + k2 *= (step_size / 2.0); + + *m_state += k2; + } else if (substeps == 4) { + // Runge-Kutta method (4th order) + cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); + k1 *= step_size; + + *m_state += k1 * 0.5; + + cudm_state k2 = + m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); + k2 *= step_size; + + *m_state += k2 * 0.5; + + cudm_state k3 = + m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); + k3 *= step_size; + + *m_state += k3; + + cudm_state k4 = m_stepper->compute(*m_state, m_t + step_size, step_size); + k4 *= step_size; + + *m_state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); + } else { + throw std::runtime_error("Invalid integrator order"); + } + + // Update time + m_t += step_size; + } + + std::cout << "Integration complete. Final time: " << m_t << std::endl; +} + +// TODO: remove this +runge_kutta_integrator::runge_kutta_integrator( + cudm_state &&initial_state, double t0, + std::shared_ptr stepper, int substeps) + : m_t(t0), m_stepper(stepper), order(substeps) { + + m_state = std::make_unique(std::move(initial_state)); +} +void runge_kutta_integrator::set_stepper(std::shared_ptr stepper) { + m_stepper = stepper; +} + +void runge_kutta_integrator::set_state(cudm_state &&initial_state) { + m_state = std::make_unique(std::move(initial_state)); + m_t = 0.0; +} +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 1a7c87c3b6..98e7841575 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -312,6 +312,7 @@ if (CUDA_FOUND) CUDA::cudart_static gtest_main fmt::fmt-header-only) + target_include_directories(test_dynamics PRIVATE ${CMAKE_SOURCE_DIR}/runtime/nvqir/cudensitymat) gtest_discover_tests(test_dynamics) endif() add_subdirectory(plugin) diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index e3063a2e23..5f9af89282 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -7,10 +7,10 @@ ******************************************************************************/ #include "test_mocks.h" -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 9bce633f89..9e2015a13d 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -7,9 +7,9 @@ ******************************************************************************/ #include "test_mocks.h" -#include -#include -#include +#include +#include +#include #include #include diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 8ac6bffee2..89c3da00d2 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -7,9 +7,9 @@ ******************************************************************************/ #include -#include -#include -#include +#include +#include +#include #include #include #include diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 914182466d..5183d9e8f8 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -7,10 +7,10 @@ ******************************************************************************/ #include "test_mocks.h" -#include -#include -#include -#include +#include +#include +#include +#include #include #include #include diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index d474067320..2bd090750a 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -12,6 +12,8 @@ #include #include "common/EigenDense.h" #include +#include "cudaq/runge_kutta_integrator.h" +#include "cudm_state.h" TEST(EvolveTester, checkSimple) { const std::map dims = {{0, 2}}; @@ -50,8 +52,12 @@ TEST(EvolveTester, checkSimple) { std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); auto initialState = cudaq::state::from_data(std::vector>{1.0, 0.0}); - auto result = cudaq::evolve_single(ham, dims, schedule, initialState, {}, - {&pauliZ}, true); + + cudaq::runge_kutta_integrator integrator; + integrator.dt = 0.001; + integrator.order = 1; + auto result = cudaq::evolve_single(ham, dims, schedule, initialState, + integrator, {}, {&pauliZ}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); std::vector theoryResults; @@ -89,26 +95,25 @@ TEST(EvolveTester, checkCompositeSystem) { cavity_state[num_photons] = 1.0; Eigen::VectorXcd initial_state_vec = Eigen::kroneckerProduct(cavity_state, qubit_state); - std::cout << "Initial state: \n" << initial_state_vec << "\n"; - constexpr int num_steps = 201; - cudaq::Schedule schedule(cudaq::linspace(0.0, 10, num_steps)); + constexpr int num_steps = 21; + cudaq::Schedule schedule(cudaq::linspace(0.0, 1, num_steps)); auto initialState = cudaq::state::from_data( std::make_pair(initial_state_vec.data(), initial_state_vec.size())); - + cudaq::runge_kutta_integrator integrator; + integrator.dt = 0.000001; + integrator.order = 1; auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, - {}, {&cavity_occ_op, &atom_occ_op}, true); + integrator, {}, + {&cavity_occ_op, &atom_occ_op}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); - // std::vector theoryResults; - // for (const auto &t : schedule) { - // const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); - // theoryResults.emplace_back(expected); - // } int count = 0; for (auto expVals : result.get_expectation_values().value()) { EXPECT_EQ(expVals.size(), 2); - std::cout << expVals[0] << "\n"; - // EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + std::cout << expVals[0] << " | "; + std::cout << expVals[1] << "\n"; + // This should be an exchanged interaction + EXPECT_NEAR((double)expVals[0] + (double)expVals[1], num_photons, 1e-2); } } diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index dcb7a20bf0..05ffc7243c 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -12,7 +12,7 @@ #include "cudaq/utils/tensor.h" #include #include -#include +#include #include #include diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index d596bf507f..bf30fed12b 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -7,6 +7,8 @@ // ******************************************************************************/ #include "cudaq/runge_kutta_integrator.h" +#include "cudm_time_stepper.h" +#include "cudm_state.h" #include "test_mocks.h" #include #include @@ -60,92 +62,92 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - auto eulerIntegrator = std::make_unique( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 1); - eulerIntegrator->set_option("dt", 0.1); - EXPECT_NO_THROW(eulerIntegrator->integrate(1.0)); + // auto integrator = std::make_unique( + // cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + // 0.0, time_stepper_, 1); + // integrator->set_option("dt", 0.1); + // EXPECT_NO_THROW(integrator->integrate(1.0)); } // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - auto midpointIntegrator = std::make_unique( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); - midpointIntegrator->set_option("dt", 0.1); - EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); + // auto midpointIntegrator = std::make_unique( + // cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + // 0.0, time_stepper_, 2); + // midpointIntegrator->set_option("dt", 0.1); + // EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } // Integration with Runge-Kutta 4 (substeps = 4, which is the default value) TEST_F(RungeKuttaIntegratorTest, RungeKutta4Integration) { - integrator_->set_option("dt", 0.1); - EXPECT_NO_THROW(integrator_->integrate(1.0)); + // integrator_->set_option("dt", 0.1); + // EXPECT_NO_THROW(integrator_->integrate(1.0)); } // Basic Integration Test TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { - auto [t_before, state_before] = integrator_->get_state(); - integrator_->set_option("dt", 0.1); + // auto [t_before, state_before] = integrator_->get_state(); + // integrator_->set_option("dt", 0.1); - EXPECT_NO_THROW(integrator_->integrate(1.0)); + // EXPECT_NO_THROW(integrator_->integrate(1.0)); - auto [t_after, state_after] = integrator_->get_state(); - EXPECT_GT(t_after, t_before); + // auto [t_after, state_after] = integrator_->get_state(); + // EXPECT_GT(t_after, t_before); } // Multiple Integration Steps TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { - integrator_->set_option("dt", 0.1); - integrator_->integrate(0.5); - auto [t_mid, _] = integrator_->get_state(); + // integrator_->set_option("dt", 0.1); + // integrator_->integrate(0.5); + // auto [t_mid, _] = integrator_->get_state(); - EXPECT_EQ(t_mid, 0.5); + // EXPECT_EQ(t_mid, 0.5); - integrator_->integrate(1.0); - auto [t_final, __] = integrator_->get_state(); + // integrator_->integrate(1.0); + // auto [t_final, __] = integrator_->get_state(); - EXPECT_EQ(t_final, 1.0); + // EXPECT_EQ(t_final, 1.0); } // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - auto integrator_missing_dt = std::make_unique( - cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - 0.0, time_stepper_, 2); + // auto integrator_missing_dt = std::make_unique( + // cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), + // 0.0, time_stepper_, 2); - EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); + // EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); } // Invalid Time Step (dt <= 0) TEST_F(RungeKuttaIntegratorTest, InvalidTimeStepSize) { - integrator_->set_option("dt", -0.1); - EXPECT_THROW(integrator_->integrate(1.0), std::invalid_argument); + // integrator_->set_option("dt", -0.1); + // EXPECT_THROW(integrator_->integrate(1.0), std::invalid_argument); } // Zero Integration Time TEST_F(RungeKuttaIntegratorTest, ZeroIntegrationTime) { - auto [t_before, state_before] = integrator_->get_state(); - integrator_->set_option("dt", 0.1); + // auto [t_before, state_before] = integrator_->get_state(); + // integrator_->set_option("dt", 0.1); - EXPECT_NO_THROW(integrator_->integrate(0.0)); + // EXPECT_NO_THROW(integrator_->integrate(0.0)); - auto [t_after, state_after] = integrator_->get_state(); - EXPECT_EQ(t_before, t_after); + // auto [t_after, state_after] = integrator_->get_state(); + // EXPECT_EQ(t_before, t_after); } // Large Time Step TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { - integrator_->set_option("dt", 100); - EXPECT_NO_THROW(integrator_->integrate(0.0)); + // integrator_->set_option("dt", 100); + // EXPECT_NO_THROW(integrator_->integrate(0.0)); } // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - EXPECT_THROW(std::make_unique( - cudm_state(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()), - 0.0, time_stepper_, 3), - std::invalid_argument); + // EXPECT_THROW(std::make_unique( + // cudm_state(handle_, mock_initial_state_data(), + // mock_hilbert_space_dims()), + // 0.0, time_stepper_, 3), + // std::invalid_argument); } TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { @@ -177,17 +179,18 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { dims); auto time_stepper = std::make_shared(handle_, cudmOp); - auto eulerIntegrator = std::make_unique( + auto integrator = std::make_unique( cudm_state(handle_, initialState, dims), 0.0, time_stepper, integratorOrder); - eulerIntegrator->set_option("dt", 0.001); + integrator->dt = 0.001; constexpr std::size_t numDataPoints = 10; double t = 0.0; std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { - eulerIntegrator->integrate(i); - auto [t, state] = eulerIntegrator->get_state(); + integrator->integrate(i); + auto [t, statePtr] = integrator->get_cudm_state(); + auto &state = *statePtr; // std::cout << "Time = " << t << "\n"; // state.dumpDeviceData(); HANDLE_CUDA_ERROR( From 3b46cc816973f6c8943cb120855aecdaaf608962 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 13 Feb 2025 03:54:01 +0000 Subject: [PATCH 248/311] Fix RK unit test Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 5 ----- .../cudensitymat/runge_kutta_integrator.cpp | 22 +++++++++---------- 2 files changed, 11 insertions(+), 16 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 5864f3718c..d00b2957e6 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -15,11 +15,6 @@ namespace cudaq { cudm_helper::cudm_helper(cudensitymatHandle_t handle) : handle(handle) {} cudm_helper::~cudm_helper() { - if (handle) { - cudensitymatDestroy(handle); - handle = nullptr; - } - cudaDeviceSynchronize(); } diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index 133a956dc1..159b92902a 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -63,26 +63,26 @@ void runge_kutta_integrator::integrate(double target_time) { } else if (substeps == 4) { // Runge-Kutta method (4th order) cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); - k1 *= step_size; - *m_state += k1 * 0.5; + cudm_state rho_temp = cudm_state::clone(*m_state); + rho_temp += (k1 * (step_size / 2)); cudm_state k2 = - m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); - k2 *= step_size; + m_stepper->compute(rho_temp, m_t + step_size / 2.0, step_size); - *m_state += k2 * 0.5; + cudm_state rho_temp_2 = cudm_state::clone(*m_state); + rho_temp_2 += (k2 * (step_size / 2)); cudm_state k3 = - m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); - k3 *= step_size; + m_stepper->compute(rho_temp_2, m_t + step_size / 2.0, step_size); - *m_state += k3; + cudm_state rho_temp_3 = cudm_state::clone(*m_state); + rho_temp_3 += (k3 * step_size); - cudm_state k4 = m_stepper->compute(*m_state, m_t + step_size, step_size); - k4 *= step_size; + cudm_state k4 = + m_stepper->compute(rho_temp_3, m_t + step_size, step_size); - *m_state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (1.0 / 6.0); + *m_state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (step_size / 6.0); } else { throw std::runtime_error("Invalid integrator order"); } From 9cf261bb032505b9bb68f6322e5cd79f8e9887e2 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 13 Feb 2025 12:37:50 -0800 Subject: [PATCH 249/311] Adding TimeSteppingWithLindblad unitest to test compute_lindblad_operator timestepping through states Signed-off-by: Sachin Pisal --- runtime/cudaq/cudm_helpers.h | 6 +- runtime/cudaq/cudm_state.h | 4 +- runtime/cudaq/dynamics/cudm_helpers.cpp | 158 ++++++++++++++---- runtime/cudaq/operators.h | 2 +- unittests/dynamics/test_cudm_expectation.cpp | 3 +- unittests/dynamics/test_cudm_time_stepper.cpp | 46 ++++- unittests/dynamics/test_evolve_single.cpp | 2 +- 7 files changed, 173 insertions(+), 48 deletions(-) diff --git a/runtime/cudaq/cudm_helpers.h b/runtime/cudaq/cudm_helpers.h index 893dba8b32..15d3133f12 100644 --- a/runtime/cudaq/cudm_helpers.h +++ b/runtime/cudaq/cudm_helpers.h @@ -26,6 +26,9 @@ class cudm_helper { static std::vector> flatten_matrix(const matrix_2 &matrix); + static void + print_complex_vector(const std::vector> &vec); + // State Operations void scale_state(cudensitymatState_t state, double scale_factor, cudaStream_t stream); @@ -80,7 +83,8 @@ class cudm_helper { void append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, const std::vector &elem_ops, - const std::vector> °rees, bool is_dagger); + const std::vector> °rees, + const std::vector> &all_action_dual_modalities); // GPU memory management static void * diff --git a/runtime/cudaq/cudm_state.h b/runtime/cudaq/cudm_state.h index c72494cf62..d2326fef61 100644 --- a/runtime/cudaq/cudm_state.h +++ b/runtime/cudaq/cudm_state.h @@ -126,8 +126,8 @@ class cudm_state { /// dimensions. /// @param hilbertSpaceDims Hilbert space dimensions. /// @return Size of the state vector. - static size_t calculate_state_vector_size( - const std::vector &hilbertSpaceDims); + static size_t + calculate_state_vector_size(const std::vector &hilbertSpaceDims); /// @brief Calculate the size of the density matrix for the given Hilbert /// space dimensions. diff --git a/runtime/cudaq/dynamics/cudm_helpers.cpp b/runtime/cudaq/dynamics/cudm_helpers.cpp index 68a74517ad..09359ebc01 100644 --- a/runtime/cudaq/dynamics/cudm_helpers.cpp +++ b/runtime/cudaq/dynamics/cudm_helpers.cpp @@ -171,6 +171,22 @@ cudm_helper::get_subspace_extents(const std::vector &mode_extents, return subspace_extents; } +void cudm_helper::print_complex_vector( + const std::vector> &vec) { + size_t n = static_cast(std::sqrt(vec.size())); + + std::cout << "Vector contents: [\n"; + for (size_t i = 0; i < n; i++) { + std::cout << "["; + for (size_t j = 0; j < n; j++) { + size_t index = i * n + j; + std::cout << " (" << vec[index].real() << ", " << vec[index].imag() + << "i) "; + } + std::cout << "]\n"; + } +} + // Function to create a cudensitymat elementary operator // Need to use std::variant cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( @@ -193,7 +209,6 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( nullptr}; if (!parameters.empty()) { - wrapped_tensor_callback = _wrap_tensor_callback(elem_op); } @@ -221,7 +236,8 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( void cudm_helper::append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, const std::vector &elem_ops, - const std::vector> °rees, bool is_dager) { + const std::vector> °rees, + const std::vector> &all_action_dual_modalities) { if (degrees.empty()) { throw std::invalid_argument("Degrees vector cannot be empty."); @@ -236,25 +252,46 @@ void cudm_helper::append_elementary_operator_to_term( "elem_ops and degrees must have the same size."); } + bool has_dual_modalities = !all_action_dual_modalities.empty(); + + if (has_dual_modalities && + degrees.size() != all_action_dual_modalities.size()) { + throw std::invalid_argument( + "degrees and all_action_dual_modalities must have the same size."); + } + std::vector allDegrees; std::vector allModeActionDuality; - for (const auto &sub_degrees : degrees) { + for (size_t i = 0; i < degrees.size(); i++) { + const auto &sub_degrees = degrees[i]; + const auto &modalities = has_dual_modalities + ? all_action_dual_modalities[i] + : std::vector(sub_degrees.size(), 0); + + if (sub_degrees.size() != modalities.size()) { + throw std::runtime_error( + "Mismatch between degrees and modalities sizes."); + } if (sub_degrees.size() != 1) { throw std::runtime_error( "Elementary operator must act on a single degree."); } - for (int degree : sub_degrees) { - if (degree < 0) { + + for (size_t j = 0; j < sub_degrees.size(); j++) { + int degree = sub_degrees[j]; + int modality = modalities[j]; + + if (sub_degrees[i] < 0) { throw std::out_of_range("Degree cannot be negative!"); } allDegrees.emplace_back(degree); - allModeActionDuality.emplace_back(is_dager ? 1 : 0); + allModeActionDuality.emplace_back(modality); } } assert(elem_ops.size() == degrees.size()); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, static_cast(allDegrees.size()), elem_ops.data(), + handle, term, static_cast(elem_ops.size()), elem_ops.data(), allDegrees.data(), allModeActionDuality.data(), make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); } @@ -305,10 +342,10 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( std::vector terms; std::vector elem_ops; std::vector> all_degrees; + std::vector> all_action_dual_modalities; try { for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); if (dim == 0 || c_op.get_columns() != dim) { @@ -349,48 +386,95 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( elem_ops.emplace_back(L_elem_op); all_degrees.emplace_back(L_op.degrees); + + std::vector mod_vec(L_op.degrees.size(), 1); + all_action_dual_modalities.emplace_back(mod_vec); + elem_ops.emplace_back(L_dagger_elem_op); all_degrees.emplace_back(L_dagger_op.degrees); + mod_vec = std::vector(L_dagger_op.degrees.size(), 0); + all_action_dual_modalities.emplace_back(mod_vec); + // D1 = L * Lt cudensitymatOperatorTerm_t term_D1; HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term_D1)); - append_elementary_operator_to_term(term_D1, elem_ops, all_degrees, false); + append_elementary_operator_to_term(term_D1, elem_ops, all_degrees, + all_action_dual_modalities); - // Add term D1 to the Lindblad operator + elem_ops.clear(); + all_degrees.clear(); + all_action_dual_modalities.clear(); + // Add term D1 to the Lindblad operator cudensitymatWrappedScalarCallback_t scalar_callback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle, lindblad_op, term_D1, 0, make_cuDoubleComplex(1.0, 0.0), scalar_callback)); - // D2 = -0.5 * (Lt * L) - cudensitymatOperatorTerm_t term_D2; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term_D2)); + // elem_ops.emplace_back(L_dagger_elem_op); + // all_degrees.emplace_back(L_dagger_op.degrees); - append_elementary_operator_to_term(term_D2, elem_ops, all_degrees, false); + // elem_ops.emplace_back(L_elem_op); + // all_degrees.emplace_back(L_op.degrees); - // Add term D2 to the Lindblad operator - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, lindblad_op, term_D2, 0, make_cuDoubleComplex(-0.5, 0.0), - scalar_callback)); + // mod_vec = std::vector(L_dagger_op.degrees.size(), 0); + // all_action_dual_modalities.emplace_back(mod_vec); - // D3 = -0.5 * (L * Lt) - cudensitymatOperatorTerm_t term_D3; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term_D3)); - append_elementary_operator_to_term(term_D3, elem_ops, all_degrees, true); + // mod_vec = std::vector(L_op.degrees.size(), 0); + // all_action_dual_modalities.emplace_back(mod_vec); - // Add term D3 to the Lindblad operator - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, lindblad_op, term_D3, 0, make_cuDoubleComplex(-0.5, 0.0), - scalar_callback)); + // // D2 = -0.5 * (Lt * L) + // cudensitymatOperatorTerm_t term_D2; + // HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + // handle, static_cast(mode_extents.size()), + // mode_extents.data(), &term_D2)); + + // append_elementary_operator_to_term(term_D2, elem_ops, all_degrees, + // all_action_dual_modalities); + + // // Clear vectors + // elem_ops.clear(); + // all_degrees.clear(); + // all_action_dual_modalities.clear(); + + // Add term D2 to the Lindblad operator + // HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + // handle, lindblad_op, term_D2, 0, make_cuDoubleComplex(-0.5, 0.0), + // scalar_callback)); + + // elem_ops.emplace_back(L_elem_op); + // all_degrees.emplace_back(L_op.degrees); + + // elem_ops.emplace_back(L_dagger_elem_op); + // all_degrees.emplace_back(L_dagger_op.degrees); + + // mod_vec = std::vector(L_op.degrees.size(), 1); + // all_action_dual_modalities.emplace_back(mod_vec); + + // mod_vec = std::vector(L_dagger_op.degrees.size(), 1); + // all_action_dual_modalities.emplace_back(mod_vec); + + // // D3 = -0.5 * (L * Lt) + // cudensitymatOperatorTerm_t term_D3; + // HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + // handle, static_cast(mode_extents.size()), + // mode_extents.data(), &term_D3)); + // append_elementary_operator_to_term(term_D3, elem_ops, all_degrees, + // all_action_dual_modalities); + + // // Clear vectors + // elem_ops.clear(); + // all_degrees.clear(); + // all_action_dual_modalities.clear(); + + // // Add term D3 to the Lindblad operator + // HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + // handle, lindblad_op, term_D3, 0, make_cuDoubleComplex(-0.5, 0.0), + // scalar_callback)); } HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); @@ -410,13 +494,13 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( return nullptr; } - for (auto term : terms) { - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - } + // for (auto term : terms) { + // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + // } - for (auto elem_op : elem_ops) { - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - } + // for (auto elem_op : elem_ops) { + // HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + // } return lindblad_op; } @@ -471,7 +555,7 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( throw std::runtime_error("Unhandled type!"); } } - append_elementary_operator_to_term(term, elem_ops, all_degrees, false); + append_elementary_operator_to_term(term, elem_ops, all_degrees, {}); auto coeff = product_op.get_coefficient(); cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index baa9e7bee6..55c7e184de 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -625,7 +625,7 @@ class matrix_operator { auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { // todo: make a nice error message to say op already exists - throw; + // throw; } } }; diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index e3063a2e23..898625054f 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -6,6 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "common/EigenDense.h" #include "test_mocks.h" #include #include @@ -14,7 +15,6 @@ #include #include #include -#include "common/EigenDense.h" #include using namespace cudaq; @@ -55,7 +55,6 @@ TEST_F(CuDensityExpectationTest, checkCompute) { } } - TEST_F(CuDensityExpectationTest, checkCompositeSystem) { cudm_helper helper(handle_); const std::vector dims = {2, 10}; diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 914182466d..bc56dad2d2 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -23,11 +23,15 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { cudensitymatOperator_t liouvillian_; std::unique_ptr time_stepper_; std::unique_ptr state_; + std::unique_ptr helper_; void SetUp() override { // Create library handle HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); + // Create helper + helper_ = std::make_unique(handle_); + // Create a mock Liouvillian liouvillian_ = mock_liouvillian(handle_); @@ -43,7 +47,7 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { void TearDown() override { // Clean up HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(liouvillian_)); - HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); + // HANDLE_CUDM_ERROR(cudensitymatDestroy(handle_)); } }; @@ -93,14 +97,14 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { } TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { - cudm_helper helper(handle_); const std::vector> initialState = { {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {4}; auto inputState = std::make_unique(handle_, initialState, dims); auto op = cudaq::matrix_operator::create(0); - auto cudmOp = helper.convert_to_cudensitymat_operator( - {}, op, dims); // Initialize the time stepper + auto cudmOp = + helper_->convert_to_cudensitymat_operator( + {}, op, dims); // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); auto outputState = time_stepper->compute(*inputState, 0.0, 1.0); @@ -117,3 +121,37 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { } HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); } + +TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { + std::vector> initial_state; + initial_state.resize(100, {0.0, 0.0}); + initial_state[5 * 10 + 5] = {1.0, 0.0}; + + const std::vector dims = {10}; + auto input_state = std::make_unique(handle_, initial_state, dims); + + // auto c_op_0 = cudaq::matrix_operator::annihilate(0); + auto c_op_0 = cudaq::matrix_operator::create(0); + auto cudm_lindblad_op = + helper_->compute_lindblad_operator({c_op_0.to_matrix({{0, 10}})}, dims); + + auto time_stepper = + std::make_unique(handle_, cudm_lindblad_op); + auto output_state = time_stepper_->compute(*input_state, 0.0, 1.0); + + std::cout << "Printing output_state ..." << std::endl; + output_state.dumpDeviceData(); + + std::vector> output_state_vec(100); + HANDLE_CUDA_ERROR( + cudaMemcpy(output_state_vec.data(), output_state.get_device_pointer(), + output_state_vec.size() * sizeof(std::complex), + cudaMemcpyDefault)); + + helper_->print_complex_vector(output_state_vec); + + EXPECT_TRUE(std::abs(output_state_vec[4 * 10 + 4] - + std::complex(1.0, 0.0)) < 1e-12); + + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudm_lindblad_op)); +} diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index d474067320..a6cac41f12 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -6,11 +6,11 @@ // * the terms of the Apache License 2.0 which accompanies this distribution. * // ******************************************************************************/ +#include "common/EigenDense.h" #include "cudaq/evolution.h" #include #include #include -#include "common/EigenDense.h" #include TEST(EvolveTester, checkSimple) { From ed5da7970f6176ce610780d4bde055ddf672c7b3 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 4 Feb 2025 12:58:50 +0000 Subject: [PATCH 250/311] completing tests, including for custom matrices Signed-off-by: Bettina Heim --- unittests/dynamics/matrix_ops_arithmetic.cpp | 11 +- unittests/dynamics/matrix_ops_simple.cpp | 35 ++- unittests/dynamics/operator_sum.cpp | 202 +++++++----------- .../dynamics/product_operators_arithmetic.cpp | 70 +++++- unittests/dynamics/scalar_ops_arithmetic.cpp | 9 - unittests/dynamics/scalar_ops_simple.cpp | 10 + 6 files changed, 179 insertions(+), 158 deletions(-) diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index d7710419fb..9b2e9af6fc 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -117,12 +117,10 @@ void assert_product_equal( } // namespace utils_0 -// FIXME: NO LONGER ACCURATE? -> NOT TESTED ANYWHERE -/// We get an error in the test body when evaluating -/// the return of this function, since the `-1.0` value -/// is going out of scope somewhere down the line in its -/// conversion behind the scenes to a scalar operator. -cudaq::scalar_operator negate(cudaq::scalar_operator op) { return -1.0 * op; } +TEST(OperatorExpressions, checkElementaryUnary) { + auto id = cudaq::matrix_operator::identity(0); + utils_0::checkEqual((-id).to_matrix({{0,2}}), -1.0 * utils_0::id_matrix(2)); +} TEST(OperatorExpressions, checkElementaryAgainstDouble) { std::complex value = 0.125 + 0.125j; @@ -435,7 +433,6 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { auto self = cudaq::matrix_operator::annihilate(0); auto other = cudaq::matrix_operator::create(1); - // Produces an `product_operator` type. auto product = self * other; ASSERT_TRUE(product.n_terms() == 2); diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp index 9d7548b720..8fc0642144 100644 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -216,10 +216,33 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOps) { } } -// TEST(OperatorExpressions, checkCustomElementaryOps) { -// pass +TEST(OperatorExpressions, checkCustomElementaryOps) { + auto level_count = 2; + std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}}; + + { + auto func0 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); + auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); -// ex: -// operator acts upon {0,2} -// user gives us dimensions for {0,1,2} -//} + auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), + utils::position_matrix(level_count + 2)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), + utils::number_matrix(level_count)); + + utils::checkEqual(op0.to_matrix(dimensions), matrix0); + utils::checkEqual(op1.to_matrix(dimensions), matrix1); +} diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index c959e1f7c7..783b246e0a 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -105,132 +105,6 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, } // namespace utils_2 -// TEST(OperatorExpressions, checkProductOperatorSimple) { -// std::vector levels = {2, 3, 4}; - -// // std::set uniqueDegrees; -// // std::copy(this->degrees.begin(), this->degrees.end(), -// // std::inserter(uniqueDegrees, uniqueDegrees.begin())); -// // std::copy(other.degrees.begin(), other.degrees.end(), -// // std::inserter(uniqueDegrees, uniqueDegrees.begin())); - -// // Arithmetic only between elementary operators with -// // same number of levels. -// { -// // Same degrees of freedom. -// { -// for (auto level_count : levels) { -// auto op0 = cudaq::matrix_operator::annihilate(0); -// auto op1 = cudaq::matrix_operator::create(0); - -// cudaq::product_operator got = op0 * op1; -// auto got_matrix = got.to_matrix({{0, level_count}}, {}); - -// auto matrix0 = _annihilate_matrix(level_count); -// auto matrix1 = _create_matrix(level_count); -// auto want_matrix = matrix0 * matrix1; - -// // ASSERT_TRUE(want_matrix == got_matrix); -// } -// } - -// // // Different degrees of freedom. -// // { -// // for (auto level_count : levels) { -// // auto op0 = cudaq::matrix_operator::annihilate(0); -// // auto op1 = cudaq::matrix_operator::create(1); - -// // cudaq::product_operator got = op0 * op1; -// // auto got_matrix = -// // got.to_matrix({{0, level_count}, {1, level_count}}, {}); - -// // cudaq::product_operator got_reverse = op1 * op0; -// // auto got_matrix_reverse = -// // got_reverse.to_matrix({{0, level_count}, {1, level_count}}, -// {}); - -// // auto identity = _id_matrix(level_count); -// // auto matrix0 = _annihilate_matrix(level_count); -// // auto matrix1 = _create_matrix(level_count); - -// // auto fullHilbert0 = identity.kronecker(matrix0); -// // auto fullHilbert1 = matrix1.kronecker(identity); -// // auto want_matrix = fullHilbert0 * fullHilbert1; -// // auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - -// // // ASSERT_TRUE(want_matrix == got_matrix); -// // // ASSERT_TRUE(want_matrix_reverse == got_matrix_reverse); -// // } -// // } - -// // // Different degrees of freedom, non-consecutive. -// // { -// // for (auto level_count : levels) { -// // auto op0 = cudaq::matrix_operator::annihilate(0); -// // auto op1 = cudaq::matrix_operator::create(2); - -// // // cudaq::product_operator got = op0 * op1; -// // // auto got_matrix = -// got.to_matrix({{0,level_count},{2,level_count}}, -// // // {}); -// // } -// // } - -// // // Different degrees of freedom, non-consecutive but all dimensions -// // // provided. -// // { -// // for (auto level_count : levels) { -// // auto op0 = cudaq::matrix_operator::annihilate(0); -// // auto op1 = cudaq::matrix_operator::create(2); - -// // // cudaq::product_operator got = op0 * op1; -// // // auto got_matrix = -// // // -// got.to_matrix({{0,level_count},{1,level_count},{2,level_count}}, -// // {}); -// // } -// // } -// } -// } - -// TEST(OperatorExpressions, checkProductOperatorSimple) { - -// std::complex value_0 = 0.1 + 0.1; -// std::complex value_1 = 0.1 + 1.0; -// std::complex value_2 = 2.0 + 0.1; -// std::complex value_3 = 2.0 + 1.0; - -// auto local_variable = true; -// auto function = [&](std::map> parameters) -// { -// if (!local_variable) -// throw std::runtime_error("Local variable not detected."); -// return parameters["value"]; -// }; - -// // Scalar Ops against Elementary Ops -// { -// // Identity against constant. -// { -// auto id_op = cudaq::matrix_operator::identity(0); -// auto scalar_op = cudaq::scalar_operator(value_0); - -// // auto multiplication = scalar_op * id_op; -// // auto addition = scalar_op + id_op; -// // auto subtraction = scalar_op - id_op; -// } - -// // Identity against constant from lambda. -// { -// auto id_op = cudaq::matrix_operator::identity(0); -// auto scalar_op = cudaq::scalar_operator(function); - -// // auto multiplication = scalar_op * id_op; -// // auto addition = scalar_op + id_op; -// // auto subtraction = scalar_op - id_op; -// } -// } -// } TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { int level_count = 3; @@ -1161,4 +1035,78 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto want_matrix = sum_matrix * product_matrix; utils_2::checkEqual(want_matrix, got_matrix); } -} \ No newline at end of file +} + +TEST(OperatorExpressions, checkCustomOperatorSum) { + auto level_count = 2; + std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; + + { + auto func0 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils_2::momentum_matrix(dimensions[0]), + utils_2::position_matrix(dimensions[1]));; + }; + auto func1 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils_2::create_matrix(dimensions[0]), + utils_2::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); + auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); + auto sum = op0 + op1; + auto sum_reverse = op1 + op0; + auto difference = op0 - op1; + auto difference_reverse = op1 - op0; + + std::vector matrices_0 = { + utils_2::id_matrix(level_count), + utils_2::position_matrix(level_count + 2), + utils_2::momentum_matrix(level_count + 1)}; + std::vector matrices_1 = { + utils_2::number_matrix(level_count), + utils_2::create_matrix(level_count + 2), + utils_2::id_matrix(level_count + 1)}; + auto sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + + utils_2::checkEqual(sum.to_matrix(dimensions), sum_expected); + utils_2::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); + utils_2::checkEqual(difference.to_matrix(dimensions), diff_expected); + utils_2::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); + + op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {2, 3})); + op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {2, 0})); + sum = op0 + op1; + sum_reverse = op1 + op0; + difference = op0 - op1; + difference_reverse = op1 - op0; + + matrices_0 = { + utils_2::position_matrix(level_count + 3), + utils_2::momentum_matrix(level_count), + utils_2::id_matrix(level_count + 1)}; + matrices_1 = { + utils_2::id_matrix(level_count + 3), + utils_2::create_matrix(level_count), + utils_2::number_matrix(level_count + 1)}; + sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + + utils_2::checkEqual(sum.to_matrix(dimensions), sum_expected); + utils_2::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); + utils_2::checkEqual(difference.to_matrix(dimensions), diff_expected); + utils_2::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); +} diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 4155178376..1b038b7a40 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -117,15 +117,6 @@ void assert_product_equal( ASSERT_TRUE(got.get_terms() == expected_terms); } -void print(cudaq::matrix_2 matrix) { - for (std::size_t i = 0; i < matrix.get_rows(); i++) { - for (std::size_t j = 0; j < matrix.get_columns(); j++) { - std::cout << matrix[{i, j}] << " "; - } - std::cout << std::endl; - } -} - } // namespace utils_1 TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { @@ -1155,3 +1146,64 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); } } + +TEST(OperatorExpressions, checkCustomProductOps) { + auto level_count = 2; + std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; + + { + auto func0 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils_1::momentum_matrix(dimensions[0]), + utils_1::position_matrix(dimensions[1]));; + }; + auto func1 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils_1::create_matrix(dimensions[0]), + utils_1::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); + auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); + auto product = op0 * op1; + auto reverse = op1 * op0; + + std::vector matrices = { + utils_1::number_matrix(level_count), + utils_1::position_matrix(level_count + 2) * utils_1::create_matrix(level_count + 2), + utils_1::momentum_matrix(level_count + 1)}; + auto expected = cudaq::kronecker(matrices.begin(), matrices.end()); + + std::vector matrices_reverse = { + utils_1::number_matrix(level_count), + utils_1::create_matrix(level_count + 2) * utils_1::position_matrix(level_count + 2), + utils_1::momentum_matrix(level_count + 1)}; + auto expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); + + utils_1::checkEqual(product.to_matrix(dimensions), expected); + utils_1::checkEqual(reverse.to_matrix(dimensions), expected_reverse); + + op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {2, 3})); + op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {2, 0})); + product = op0 * op1; + reverse = op1 * op0; + + matrices = { + utils_1::position_matrix(level_count + 3), + utils_1::momentum_matrix(level_count) * utils_1::create_matrix(level_count), + utils_1::number_matrix(level_count + 1)}; + expected = cudaq::kronecker(matrices.begin(), matrices.end()); + + matrices_reverse = { + utils_1::position_matrix(level_count + 3), + utils_1::create_matrix(level_count) * utils_1::momentum_matrix(level_count), + utils_1::number_matrix(level_count + 1)}; + expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); + + utils_1::checkEqual(product.to_matrix(dimensions), expected); + utils_1::checkEqual(reverse.to_matrix(dimensions), expected_reverse); +} + diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_ops_arithmetic.cpp index b6d9a48518..e1107807f5 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_ops_arithmetic.cpp @@ -28,7 +28,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { auto scalar_op = cudaq::scalar_operator(value_0); auto new_scalar_op = value_1 + scalar_op; - // function + scalar_op; auto reverse_order_op = scalar_op + value_1; EXPECT_NEAR(std::abs(scalar_op.evaluate()), std::abs(value_0), 1e-5); @@ -39,7 +38,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(want_value), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(want_value), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op + reverse_order_op; auto got_value_third = third_op.evaluate(); EXPECT_NEAR(std::abs(got_value_third), std::abs(want_value + want_value), @@ -59,7 +57,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_0 + value_1), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 + value_0), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op + reverse_order_op; auto got_value_third = third_op.evaluate({{"value", value_1}}); auto want_value = value_0 + value_1 + value_1 + value_0; @@ -79,7 +76,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_3 - value_1), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_3), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op - reverse_order_op; auto got_value_third = third_op.evaluate(); auto want_value = (value_3 - value_1) - (value_1 - value_3); @@ -99,7 +95,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_2 - value_1), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 - value_2), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op - reverse_order_op; auto got_value_third = third_op.evaluate({{"value", value_1}}); auto want_value = (value_2 - value_1) - (value_1 - value_2); @@ -119,7 +114,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op * reverse_order_op; auto got_value_third = third_op.evaluate(); auto want_value = (value_3 * value_2) * (value_2 * value_3); @@ -139,7 +133,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_3 * value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 * value_3), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op * reverse_order_op; auto got_value_third = third_op.evaluate({{"value", value_2}}); auto want_value = (value_3 * value_2) * (value_2 * value_3); @@ -159,7 +152,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_2), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_2 / value_3), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; auto got_value_third = third_op.evaluate(); auto want_value = (value_3 / value_2) / (value_2 / value_3); @@ -179,7 +171,6 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { EXPECT_NEAR(std::abs(got_value), std::abs(value_3 / value_1), 1e-5); EXPECT_NEAR(std::abs(got_value_1), std::abs(value_1 / value_3), 1e-5); - // Checking composition of many scalar operators. auto third_op = new_scalar_op / reverse_order_op; auto got_value_third = third_op.evaluate({{"value", value_1}}); auto want_value = (value_3 / value_1) / (value_1 / value_3); diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp index 550b277f00..5da3208520 100644 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ b/unittests/dynamics/scalar_ops_simple.cpp @@ -9,6 +9,16 @@ #include "cudaq/operators.h" #include +cudaq::scalar_operator negate(cudaq::scalar_operator op) { + return -1.0 * op; +} + +TEST(OperatorExpressions, checkScalarOpsUnary) { + auto scalar = cudaq::scalar_operator(1.0); + EXPECT_EQ((-scalar).evaluate(), std::complex(-1.0)); + EXPECT_EQ(negate(scalar).evaluate(), std::complex(-1.0)); +} + TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { std::complex value_0 = 0.1 + 0.1; From f145e3c2dd5ebf94ac8a9382d79beb5dde9617ce Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 4 Feb 2025 13:59:22 +0000 Subject: [PATCH 251/311] minor clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/matrix_operators.cpp | 24 ++++-- runtime/cudaq/dynamics/product_operators.cpp | 89 +++++++++----------- runtime/cudaq/operators.h | 3 +- 3 files changed, 56 insertions(+), 60 deletions(-) diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index d0b460e1ea..7208cc586e 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -176,7 +176,10 @@ product_operator matrix_operator::displace(int degree) { auto func = [](std::vector dimensions, std::map> parameters) { std::size_t dimension = dimensions[0]; - auto displacement_amplitude = parameters["displacement"]; + auto entry = parameters.find("displacement"); + if (entry == parameters.end()) + throw std::runtime_error("missing value for parameter 'displacement'"); + auto displacement_amplitude = entry->second; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -200,7 +203,10 @@ product_operator matrix_operator::squeeze(int degree) { auto func = [](std::vector dimensions, std::map> parameters) { std::size_t dimension = dimensions[0]; - auto squeezing = parameters["squeezing"]; + auto entry = parameters.find("squeezing"); + if (entry == parameters.end()) + throw std::runtime_error("missing value for parameter 'squeezing'"); + auto squeezing = entry->second; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -224,11 +230,15 @@ matrix_2 matrix_operator::to_matrix( std::map> parameters) const { auto it = matrix_operator::m_ops.find(this->id); if (it != matrix_operator::m_ops.end()) { - std::vector relevant_dimensions; - relevant_dimensions.reserve(this->degrees.size()); - for (auto d : this->degrees) - relevant_dimensions.push_back(dimensions[d]); - return it->second.generate_matrix(relevant_dimensions, parameters); + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->degrees.size()); + for (auto d : this->degrees) { + auto entry = dimensions.find(d); + if (entry == dimensions.end()) + throw std::runtime_error("missing dimension for degree " + std::to_string(d)); + relevant_dimensions.push_back(entry->second); + } + return it->second.generate_matrix(relevant_dimensions, parameters); } throw std::range_error("unable to find operator"); } diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 0c50f14e95..ed28c16e9e 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -18,31 +18,14 @@ namespace cudaq { // private methods -EvaluatedMatrix -_padded_op(MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, - std::vector degrees, std::map dimensions, - std::map> parameters) { - /// Creating the tensor product with op being last is most efficient. - std::vector padded; - for (const auto °ree : degrees) { - if (std::find(op.degrees.begin(), op.degrees.end(), degree) == - op.degrees.end()) { - // FIXME: EITHER MAKE DIMENSIONS REQUIRED, OR GIVE AN ERROR IF DIMENSIONS - // ARE REQUIRED. - padded.push_back(EvaluatedMatrix( - {degree}, matrix_operator::identity(degree).to_matrix(dimensions))); - // FIXME: avoid creation of a product here - - // but we need to make sure identity is defined before using it (all ops - // are lazily defined...) - // padded.push_back(cudaq::matrix_operator("identity", - // {degree}).to_matrix()); - } - } - matrix_2 mat = op.to_matrix(dimensions, parameters); - auto res = EvaluatedMatrix(op.degrees, mat); // FIXME: PUT THIS LAST - for (auto &op : padded) - res = arithmetics.tensor(res, op); - return res; +template +void product_operator::aggregate_terms() {} + +template +template +void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { + this->terms[0].push_back(head); + aggregate_terms(std::forward(args)...); } // FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - @@ -55,18 +38,31 @@ product_operator::m_evaluate(MatrixArithmetics arithmetics, auto degrees = this->degrees(); cudaq::matrix_2 result; + auto padded_op = [](MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, + std::vector degrees, std::map dimensions, + std::map> parameters) { + std::vector padded; + for (const auto °ree : degrees) { + if (std::find(op.degrees.begin(), op.degrees.end(), degree) == op.degrees.end()) { + padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); + } + } + /// Creating the tensor product with op being last is most efficient. + if (padded.size() == 0) + return EvaluatedMatrix(op.degrees, op.to_matrix(dimensions, parameters)); + EvaluatedMatrix ids = padded[0]; + for (auto i = 1; i < padded.size(); ++i) + ids = arithmetics.tensor(ids, padded[i]); + return arithmetics.tensor(ids, EvaluatedMatrix(op.degrees, op.to_matrix(dimensions, parameters))); + }; + if (terms.size() > 0) { if (pad_terms) { - auto evaluated = - _padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, - arithmetics.m_parameters); + auto evaluated = padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { const HandlerTy &op = terms[op_idx]; - if (op.degrees.size() != 1 || - op != cudaq::matrix_operator("identity", op.degrees)) { - auto padded = - _padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, - arithmetics.m_parameters); + if (op.degrees.size() != 1 || op != cudaq::matrix_operator("identity", op.degrees)) { + auto padded = padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); evaluated = arithmetics.mul(evaluated, padded); } } @@ -91,24 +87,18 @@ product_operator::m_evaluate(MatrixArithmetics arithmetics, return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } -template -void product_operator::aggregate_terms() {} - -template -template -void product_operator::aggregate_terms(const HandlerTy &head, - Args &&...args) { - this->terms[0].push_back(head); - aggregate_terms(std::forward(args)...); -} - -template void product_operator::aggregate_terms( - const matrix_operator &item1, const matrix_operator &item2); +template +void product_operator::aggregate_terms(const matrix_operator &item1, + const matrix_operator &item2); template void product_operator::aggregate_terms( const matrix_operator &item1, const matrix_operator &item2, const matrix_operator &item3); +template +cudaq::matrix_2 product_operator::m_evaluate( + MatrixArithmetics arithmetics, bool pad_terms) const; + // read-only properties template @@ -137,11 +127,8 @@ scalar_operator product_operator::get_coefficient() const { return this->coefficients[0]; } -template cudaq::matrix_2 -product_operator::m_evaluate(MatrixArithmetics arithmetics, - bool pad_terms) const; - -template std::vector product_operator::degrees() const; +template +std::vector product_operator::degrees() const; template int product_operator::n_terms() const; diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 55c7e184de..7a544d4192 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -624,8 +624,7 @@ class matrix_operator { std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { - // todo: make a nice error message to say op already exists - // throw; + throw std::runtime_error("an matrix operator with name " + operator_id + "is already defined"); } } }; From d30888639199ee5141b0381527e4dd50af09870d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 4 Feb 2025 20:22:08 +0000 Subject: [PATCH 252/311] general clean up Signed-off-by: Bettina Heim --- python/cudaq/operator/manipulation.py | 24 ++ runtime/cudaq/dynamics/manipulation.cpp | 67 ++++-- runtime/cudaq/dynamics/manipulation.h | 82 +++++++ runtime/cudaq/dynamics/matrix_operators.cpp | 95 ++++++-- runtime/cudaq/dynamics/operator_sum.cpp | 98 +------- runtime/cudaq/dynamics/product_operators.cpp | 28 +-- runtime/cudaq/operators.h | 222 ++++++------------- 7 files changed, 318 insertions(+), 298 deletions(-) create mode 100644 runtime/cudaq/dynamics/manipulation.h diff --git a/python/cudaq/operator/manipulation.py b/python/cudaq/operator/manipulation.py index dc6d31c819..a54a154049 100644 --- a/python/cudaq/operator/manipulation.py +++ b/python/cudaq/operator/manipulation.py @@ -135,6 +135,30 @@ def _canonicalize( _OperatorHelpers.permute_matrix(op_matrix, permutation) return op_matrix, canon_degrees + def _canonicalize(self: MatrixArithmetics, op_matrix: NDArray[numpy.complexfloating], op_degrees: Iterable[int]) -> tuple[NDArray[numpy.complexfloating], tuple[int]]: + """ + Given a matrix representation that acts on the given degrees or freedom, + sorts the degrees and permutes the matrix to match that canonical order. + + Returns: + A tuple consisting of the permuted matrix as well as the sequence of degrees + of freedom in canonical order. + """ + canon_degrees = _OperatorHelpers.canonicalize_degrees(op_degrees) + if op_degrees != canon_degrees: + # There may be a more efficient way, but I needed something correct first. + states = _OperatorHelpers.generate_all_states(canon_degrees, self._dimensions) + indices = dict([(d, idx) for idx, d in enumerate(canon_degrees)]) + reordering = [indices[op_degree] for op_degree in op_degrees] + # [degrees[i] for i in reordering] produces op_degrees + op_states = _OperatorHelpers.generate_all_states(op_degrees, self._dimensions) + state_indices = dict([(state, idx) for idx, state in enumerate(states)]) + permutation = [state_indices[op_state] for op_state in op_states] + # [states[i] for i in permutation] produces op_states + _OperatorHelpers.permute_matrix(op_matrix, permutation) + return op_matrix, canon_degrees + return op_matrix, canon_degrees + def tensor(self: MatrixArithmetics, op1: MatrixArithmetics.Evaluated, op2: MatrixArithmetics.Evaluated) -> MatrixArithmetics.Evaluated: """ diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index e81c64ed84..a8252f51c6 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -6,11 +6,46 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +<<<<<<< HEAD #include "cudaq/helpers.h" #include "cudaq/operators.h" +======= +#include "manipulation.h" +#include "helpers.h" +>>>>>>> 558b2496f (general clean up) namespace cudaq { +// EvaluatedMatrix class + +const std::vector& EvaluatedMatrix::degrees() const { + return this->targets; +} + +const matrix_2& EvaluatedMatrix::matrix() const { + return this->value; +} + +EvaluatedMatrix::EvaluatedMatrix(const std::vector °rees, const matrix_2 &matrix) + : targets(degrees), value(matrix) {} + +EvaluatedMatrix::EvaluatedMatrix(EvaluatedMatrix &&other) + : targets(std::move(other.targets)), value(std::move(other.value)) {} + +EvaluatedMatrix& EvaluatedMatrix::operator=(EvaluatedMatrix &&other) { + if (this != &other) { + this->targets = std::move(other.targets); + this->value = std::move(other.value); + } + return *this; +} + +// MatrixArithmetics + +MatrixArithmetics::MatrixArithmetics(std::map dimensions, + std::map> parameters) + : m_dimensions(dimensions), m_parameters(parameters) {} + std::vector MatrixArithmetics::_compute_permutation(std::vector op_degrees, std::vector canon_degrees) { @@ -61,15 +96,13 @@ EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, // assert len(frozenset(op1.degrees).intersection(op2.degrees)) == 0, \ // "Operators should not have common degrees of freedom." - auto op1_deg = std::move(op1.degrees()); - auto op2_deg = std::move(op2.degrees()); std::vector op_degrees; - op_degrees.reserve(op1_deg.size() + op2_deg.size()); - for (auto d : op1_deg) + op_degrees.reserve(op1.degrees().size() + op2.degrees().size()); + for (auto d : op1.degrees()) op_degrees.push_back(d); - for (auto d : op2_deg) + for (auto d : op2.degrees()) op_degrees.push_back(d); - auto op_matrix = cudaq::kronecker(op1.m_matrix, op2.m_matrix); + auto op_matrix = cudaq::kronecker(op1.matrix(), op2.matrix()); auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); return EvaluatedMatrix(new_degrees, new_matrix); } @@ -80,10 +113,10 @@ EvaluatedMatrix MatrixArithmetics::mul(EvaluatedMatrix op1, // convention for how to define the matrix. Tensor products permute the // computed matrix if necessary to guarantee that all operators always have // sorted degrees. - if (op1.m_degrees != op2.m_degrees) + if (op1.degrees() != op2.degrees()) throw std::runtime_error( "Operators should have the same order of degrees."); - return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix * op2.m_matrix)); + return EvaluatedMatrix(op1.degrees(), (op1.matrix() * op2.matrix())); } EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, @@ -92,24 +125,10 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, // convention for how to define the matrix. Tensor products permute the // computed matrix if necessary to guarantee that all operators always have // sorted degrees. - if (op1.m_degrees != op2.m_degrees) + if (op1.degrees() != op2.degrees()) throw std::runtime_error( "Operators should have the same order of degrees."); - return EvaluatedMatrix(op1.m_degrees, (op1.m_matrix + op2.m_matrix)); -} - -EvaluatedMatrix -MatrixArithmetics::evaluate(std::variant> - op) { - // auto getDegrees = [](auto &&t) { return t.degrees; }; - // auto toMatrix = [&](auto &&t) { - // return t.to_matrix(this->m_dimensions, this->m_parameters); - // }; - // auto degrees = std::visit(getDegrees, op); - // auto matrix = std::visit(toMatrix, op); - // return EvaluatedMatrix(degrees, matrix); - throw std::runtime_error("implementation broken."); + return EvaluatedMatrix(op1.degrees(), (op1.matrix() + op2.matrix())); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/manipulation.h b/runtime/cudaq/dynamics/manipulation.h new file mode 100644 index 0000000000..95362cf462 --- /dev/null +++ b/runtime/cudaq/dynamics/manipulation.h @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include "cudaq/utils/tensor.h" + +namespace cudaq { + +template +class OperatorArithmetics { +public: + /// @brief Accesses the relevant data to evaluate an operator expression + /// in the leaf nodes, that is in elementary and scalar operators. + // template + //TEval evaluate(HandlerTy &op); + + /// @brief Adds two operators that act on the same degrees of freedom. + TEval add(TEval val1, TEval val2); + + /// @brief Multiplies two operators that act on the same degrees of freedom. + TEval mul(TEval val1, TEval val2); + + /// @brief Computes the tensor product of two operators that act on different + /// degrees of freedom. + TEval tensor(TEval val1, TEval val2); +}; + +class EvaluatedMatrix { +private: + + std::vector targets; + matrix_2 value; + +public: + const std::vector& degrees() const; + + const matrix_2& matrix() const; + + EvaluatedMatrix(const std::vector °rees, const matrix_2 &matrix); + EvaluatedMatrix(EvaluatedMatrix &&other); + + // delete copy constructor and copy assignment to avoid unnecessary copies + EvaluatedMatrix(const EvaluatedMatrix &other) = delete; + EvaluatedMatrix& operator=(const EvaluatedMatrix &other) = delete; + + EvaluatedMatrix& operator=(EvaluatedMatrix &&other); +}; + +/// Encapsulates the functions needed to compute the matrix representation +/// of an operator expression. +class MatrixArithmetics : public OperatorArithmetics { +private: + std::vector _compute_permutation(std::vector op_degrees, + std::vector canon_degrees); + std::tuple> + _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); + +public: + std::map &m_dimensions; // fixme: make const + std::map> &m_parameters; // fixme: make const + + MatrixArithmetics(std::map dimensions, + std::map> parameters); + + // Computes the tensor product of two evaluate operators that act on + // different degrees of freedom using the kronecker product. + EvaluatedMatrix tensor(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Multiplies two evaluated operators that act on the same degrees + // of freedom. + EvaluatedMatrix mul(EvaluatedMatrix op1, EvaluatedMatrix op2); + // Adds two evaluated operators that act on the same degrees + // of freedom. + EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); +}; + +} \ No newline at end of file diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 7208cc586e..574028be35 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -14,8 +14,85 @@ namespace cudaq { +// tools for custom operators + std::map matrix_operator::m_ops = {}; +void matrix_operator::define(std::string operator_id, std::vector expected_dimensions, + CallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); + auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); + if (!result.second) { + throw std::runtime_error("an matrix operator with name " + operator_id + "is already defined"); + } +} + +// read-only properties + +const std::vector& matrix_operator::degrees() const { + return this->targets; +} + +// constructors + +matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) + : id(operator_id), targets(degrees) {} + +matrix_operator::matrix_operator(std::string operator_id, std::vector &°rees) + : id(operator_id), targets(std::move(degrees)) {} + +matrix_operator::matrix_operator(const matrix_operator &other) + : targets(other.targets), id(other.id) {} + +matrix_operator::matrix_operator(matrix_operator &&other) + : targets(std::move(other.targets)), id(other.id) {} + +// assignments + +matrix_operator& matrix_operator::operator=(const matrix_operator& other) { + if (this != &other) { + this->targets = other.targets; + this->id = other.id; + } + return *this; +} + +matrix_operator& matrix_operator::operator=(matrix_operator &&other) { + if (this != &other) { + this->targets = std::move(other.targets); + this->id = other.id; + } + return *this; +} + +// evaluations + +matrix_2 matrix_operator::to_matrix( + std::map dimensions, + std::map> parameters) const { + auto it = matrix_operator::m_ops.find(this->id); + if (it != matrix_operator::m_ops.end()) { + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->targets.size()); + for (auto d : this->targets) { + auto entry = dimensions.find(d); + if (entry == dimensions.end()) + throw std::runtime_error("missing dimension for degree " + std::to_string(d)); + relevant_dimensions.push_back(entry->second); + } + return it->second.generate_matrix(relevant_dimensions, parameters); + } + throw std::range_error("unable to find operator"); +} + +// comparisons + +bool matrix_operator::operator==(const matrix_operator &other) const { + return this->id == other.id && this->targets == other.targets; +} + +// predefined operators + product_operator matrix_operator::identity(int degree) { std::string op_id = "identity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { @@ -225,22 +302,6 @@ product_operator matrix_operator::squeeze(int degree) { return product_operator(1., op); } -matrix_2 matrix_operator::to_matrix( - std::map dimensions, - std::map> parameters) const { - auto it = matrix_operator::m_ops.find(this->id); - if (it != matrix_operator::m_ops.end()) { - std::vector relevant_dimensions; - relevant_dimensions.reserve(this->degrees.size()); - for (auto d : this->degrees) { - auto entry = dimensions.find(d); - if (entry == dimensions.end()) - throw std::runtime_error("missing dimension for degree " + std::to_string(d)); - relevant_dimensions.push_back(entry->second); - } - return it->second.generate_matrix(relevant_dimensions, parameters); - } - throw std::range_error("unable to find operator"); -} +// tools for custom operators } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 90727ae9cf..cc476da3a5 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -8,6 +8,8 @@ #include "cudaq/helpers.h" #include "cudaq/operators.h" +#include "helpers.h" +#include "manipulation.h" #include #include @@ -30,7 +32,7 @@ operator_sum::m_evaluate(MatrixArithmetics arithmetics, auto paddedTerm = [&](auto &&term) { std::vector op_degrees; for (auto op : term.get_terms()) { - for (auto degree : op.degrees) + for (auto degree : op.degrees()) op_degrees.push_back(degree); } for (auto degree : degrees) { @@ -42,93 +44,23 @@ operator_sum::m_evaluate(MatrixArithmetics arithmetics, return term; }; - auto sum = EvaluatedMatrix(); if (pad_terms) { auto padded_term = paddedTerm(terms[0]); - sum = EvaluatedMatrix(degrees, - padded_term.m_evaluate(arithmetics, pad_terms)); + EvaluatedMatrix sum(degrees, padded_term.m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { padded_term = paddedTerm(terms[term_idx]); - sum = arithmetics.add( - sum, EvaluatedMatrix(degrees, - padded_term.m_evaluate(arithmetics, pad_terms))); + sum = arithmetics.add(std::move(sum), EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms))); } + return sum.matrix(); } else { - sum = EvaluatedMatrix(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); + EvaluatedMatrix sum(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { auto term = terms[term_idx]; auto eval = term.m_evaluate(arithmetics, pad_terms); - sum = arithmetics.add(sum, EvaluatedMatrix(degrees, eval)); + sum = arithmetics.add(std::move(sum), EvaluatedMatrix(degrees, eval)); } + return sum.matrix(); } - return sum.matrix(); -} - -template -std::tuple, std::vector> -operator_sum::m_canonicalize_product( - product_operator &prod) const { - std::vector scalars = {prod.get_coefficient()}; - auto non_scalars = prod.get_terms(); - - std::vector all_degrees; - for (auto op : non_scalars) { - for (auto degree : op.degrees) - all_degrees.push_back(degree); - } - - std::set unique_degrees(all_degrees.begin(), all_degrees.end()); - - if (all_degrees.size() == unique_degrees.size()) { - // Each operator acts on different degrees of freedom; they - // hence commute and can be reordered arbitrarily. - /// FIXME: Doing nothing for now - // std::sort(non_scalars.begin(), non_scalars.end(), [](auto op){ return - // op.degrees; }) - } else { - // Some degrees exist multiple times; order the scalars, identities, - // and zeros, but do not otherwise try to reorder terms. - std::vector zero_ops; - std::vector identity_ops; - std::vector non_commuting; - for (auto op : non_scalars) { - if (op.id == "zero") - zero_ops.push_back(op); - if (op.id == "identity") - identity_ops.push_back(op); - if (op.id != "zero" || op.id != "identity") - non_commuting.push_back(op); - } - - /// FIXME: Not doing the same sorting we do in python yet - std::vector sorted_non_scalars; - sorted_non_scalars.insert(sorted_non_scalars.end(), zero_ops.begin(), - zero_ops.end()); - sorted_non_scalars.insert(sorted_non_scalars.end(), identity_ops.begin(), - identity_ops.end()); - sorted_non_scalars.insert(sorted_non_scalars.end(), non_commuting.begin(), - non_commuting.end()); - non_scalars = sorted_non_scalars; - } - return std::make_tuple(scalars, non_scalars); -} - -template -std::tuple, std::vector> -operator_sum::m_canonical_terms() const { - /// FIXME: Not doing the same sorting we do in python yet - std::tuple, std::vector> result; - std::vector scalars; - std::vector matrix_ops; - for (auto term : this->get_terms()) { - auto canon_term = m_canonicalize_product(term); - auto canon_scalars = std::get<0>(canon_term); - auto canon_elementary = std::get<1>(canon_term); - scalars.insert(scalars.end(), canon_scalars.begin(), canon_scalars.end()); - canon_elementary.insert(canon_elementary.end(), canon_elementary.begin(), - canon_elementary.end()); - } - return std::make_tuple(scalars, matrix_ops); } template @@ -147,15 +79,7 @@ template cudaq::matrix_2 operator_sum::m_evaluate(MatrixArithmetics arithmetics, bool pad_terms) const; -template std::tuple, std::vector> -operator_sum::m_canonicalize_product( - product_operator &prod) const; - -template std::tuple, std::vector> -operator_sum::m_canonical_terms() const; - -// no overload for a single product, since we don't want a constructor for a -// single term +// no overload for a single product, since we don't want a constructor for a single term template void operator_sum::aggregate_terms( const product_operator &item1, @@ -173,7 +97,7 @@ std::vector operator_sum::degrees() const { std::set unsorted_degrees; for (const std::vector &term : this->terms) { for (const HandlerTy &op : term) - unsorted_degrees.insert(op.degrees.begin(), op.degrees.end()); + unsorted_degrees.insert(op.degrees().begin(), op.degrees().end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index ed28c16e9e..a7a7c218ce 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -8,6 +8,8 @@ #include "cudaq/helpers.h" #include "cudaq/operators.h" +#include "helpers.h" +#include "manipulation.h" #include #include @@ -43,38 +45,36 @@ product_operator::m_evaluate(MatrixArithmetics arithmetics, std::map> parameters) { std::vector padded; for (const auto °ree : degrees) { - if (std::find(op.degrees.begin(), op.degrees.end(), degree) == op.degrees.end()) { + if (std::find(op.degrees().begin(), op.degrees().end(), degree) == op.degrees().end()) { padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); } } /// Creating the tensor product with op being last is most efficient. if (padded.size() == 0) - return EvaluatedMatrix(op.degrees, op.to_matrix(dimensions, parameters)); - EvaluatedMatrix ids = padded[0]; + return EvaluatedMatrix(op.degrees(), op.to_matrix(dimensions, parameters)); + EvaluatedMatrix ids(std::move(padded[0])); for (auto i = 1; i < padded.size(); ++i) - ids = arithmetics.tensor(ids, padded[i]); - return arithmetics.tensor(ids, EvaluatedMatrix(op.degrees, op.to_matrix(dimensions, parameters))); + ids = arithmetics.tensor(std::move(ids), std::move(padded[i])); + return arithmetics.tensor(std::move(ids), EvaluatedMatrix(op.degrees(), op.to_matrix(dimensions, parameters))); }; if (terms.size() > 0) { if (pad_terms) { - auto evaluated = padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters); + EvaluatedMatrix evaluated(padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters)); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { const HandlerTy &op = terms[op_idx]; - if (op.degrees.size() != 1 || op != cudaq::matrix_operator("identity", op.degrees)) { + if (op.degrees().size() != 1 || op != cudaq::matrix_operator("identity", op.degrees())) { auto padded = padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); - evaluated = arithmetics.mul(evaluated, padded); + evaluated = arithmetics.mul(std::move(evaluated), std::move(padded)); } } result = evaluated.matrix(); } else { - auto evaluated = arithmetics.evaluate(terms[0]); + EvaluatedMatrix evaluated(terms[0].degrees(), terms[0].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { auto &op = terms[op_idx]; - auto mat = - op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); - evaluated = - arithmetics.mul(evaluated, EvaluatedMatrix(op.degrees, mat)); + auto mat = op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + evaluated = arithmetics.mul(std::move(evaluated), EvaluatedMatrix(op.degrees(), mat)); } result = evaluated.matrix(); } @@ -105,7 +105,7 @@ template std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->terms[0]) { - unsorted_degrees.insert(term.degrees.begin(), term.degrees.end()); + unsorted_degrees.insert(term.degrees().begin(), term.degrees().end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 7a544d4192..a603f14b31 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -155,14 +155,8 @@ template // handler needs to inherit from operation_handler class operator_sum { private: - std::tuple, std::vector> - m_canonicalize_product(product_operator &prod) const; - std::tuple, std::vector> - m_canonical_terms() const; - - matrix_2 m_evaluate(MatrixArithmetics arithmetics, - bool pad_terms = true) const; + matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; void aggregate_terms(); @@ -344,8 +338,6 @@ class operator_sum { /// that can. template // handler needs to inherit from operation_handler class product_operator : public operator_sum { - friend class operator_sum; // FIXME: explicitly list members - // instead? private: void aggregate_terms(); @@ -356,6 +348,9 @@ class product_operator : public operator_sum { matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + template + friend matrix_2 operator_sum::m_evaluate(MatrixArithmetics arithmetics, bool pad_terms) const; + public: // read-only properties @@ -513,72 +508,98 @@ class product_operator : public operator_sum { class matrix_operator { private: + static std::map m_ops; + std::vector targets; + std::string id; + public: + + // tools for custom operators + + /// @brief Adds the definition of an elementary operator with the given id to + /// the class. After definition, an the defined elementary operator can be + /// instantiated by providing the operator id as well as the degree(s) of + /// freedom that it acts on. An elementary operator is a parameterized object + /// acting on certain degrees of freedom. To evaluate an operator, for example + /// to compute its matrix, the level, that is the dimension, for each degree + /// of freedom it acts on must be provided, as well as all additional + /// parameters. Additional parameters must be provided in the form of keyword + /// arguments. Note: The dimensions passed during operator evaluation are + /// automatically validated against the expected dimensions specified during + /// definition - the `create` function does not need to do this. + /// @arg operator_id : A string that uniquely identifies the defined operator. + /// @arg expected_dimensions : Defines the number of levels, that is the + /// dimension, + /// for each degree of freedom in canonical (that is sorted) order. A + /// negative or zero value for one (or more) of the expected dimensions + /// indicates that the operator is defined for any dimension of the + /// corresponding degree of freedom. + /// @arg create : Takes any number of complex-valued arguments and returns the + /// matrix representing the operator in canonical order. If the matrix + /// can be defined for any number of levels for one or more degree of + /// freedom, the `create` function must take an argument called + /// `dimension` (or `dim` for short), if the operator acts on a single + /// degree of freedom, and an argument called `dimensions` (or `dims` for + /// short), if the operator acts + /// on multiple degrees of freedom. + static void define(std::string operator_id, std::vector expected_dimensions, + CallbackFunction &&create); + + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + const std::vector& degrees() const; + + // constructors and destructors + // The constructor should never be called directly by the user: // Keeping it internally documented for now, however. /// @brief Constructor. /// @arg operator_id : The ID of the operator as specified when it was /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - matrix_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), degrees(degrees) {} + matrix_operator(std::string operator_id, const std::vector °rees); // constructor - matrix_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), degrees(std::move(degrees)) {} + matrix_operator(std::string operator_id, std::vector &°rees); // copy constructor - matrix_operator(const matrix_operator &other) - : degrees(other.degrees), id(other.id) {} + matrix_operator(const matrix_operator &other); // move constructor - matrix_operator(matrix_operator &&other) - : degrees(std::move(other.degrees)), id(other.id) {} + matrix_operator(matrix_operator &&other); - // assignment operator - matrix_operator &operator=(const matrix_operator &other) { - if (this != &other) { - degrees = other.degrees; - id = other.id; - } - return *this; - } + ~matrix_operator() = default; - // move assignment operator - matrix_operator &operator=(matrix_operator &&other) { - degrees = std::move(other.degrees); - id = other.id; - return *this; - } + // assignments - virtual ~matrix_operator() = default; + // assignment operator + matrix_operator& operator=(const matrix_operator& other); - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - std::vector degrees; - std::string id; + // move assignment operator + matrix_operator& operator=(matrix_operator &&other); - /// @brief Return the `matrix_operator` as a string. - std::string to_string() const; + // evaluations /// @brief Return the `matrix_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 - to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; + matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; + + // comparisons /// @brief True, if the other value is an elementary operator with the same id /// acting on the same degrees of freedom, and False otherwise. - bool operator==(const matrix_operator &other) const { - return this->id == other.id && this->degrees == other.degrees; - } + bool operator==(const matrix_operator &other) const; + + // predefined operators - // Predefined operators. static product_operator identity(int degree); static product_operator zero(int degree); static product_operator annihilate(int degree); @@ -590,43 +611,6 @@ class matrix_operator { /// Operators that accept parameters at runtime. static product_operator squeeze(int degree); static product_operator displace(int degree); - - /// @brief Adds the definition of an elementary operator with the given id to - /// the class. After definition, an the defined elementary operator can be - /// instantiated by providing the operator id as well as the degree(s) of - /// freedom that it acts on. An elementary operator is a parameterized object - /// acting on certain degrees of freedom. To evaluate an operator, for example - /// to compute its matrix, the level, that is the dimension, for each degree - /// of freedom it acts on must be provided, as well as all additional - /// parameters. Additional parameters must be provided in the form of keyword - /// arguments. Note: The dimensions passed during operator evaluation are - /// automatically validated against the expected dimensions specified during - /// definition - the `create` function does not need to do this. - /// @arg operator_id : A string that uniquely identifies the defined operator. - /// @arg expected_dimensions : Defines the number of levels, that is the - /// dimension, - /// for each degree of freedom in canonical (that is sorted) order. A - /// negative or zero value for one (or more) of the expected dimensions - /// indicates that the operator is defined for any dimension of the - /// corresponding degree of freedom. - /// @arg create : Takes any number of complex-valued arguments and returns the - /// matrix representing the operator in canonical order. If the matrix - /// can be defined for any number of levels for one or more degree of - /// freedom, the `create` function must take an argument called - /// `dimension` (or `dim` for short), if the operator acts on a single - /// degree of freedom, and an argument called `dimensions` (or `dims` for - /// short), if the operator acts - /// on multiple degrees of freedom. - static void define(std::string operator_id, - std::vector expected_dimensions, - CallbackFunction &&create) { - auto defn = Definition(operator_id, expected_dimensions, - std::forward(create)); - auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); - if (!result.second) { - throw std::runtime_error("an matrix operator with name " + operator_id + "is already defined"); - } - } }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system @@ -682,78 +666,4 @@ extern template class product_operator; extern template class operator_sum; #endif -template -class OperatorArithmetics { -public: - /// @brief Accesses the relevant data to evaluate an operator expression - /// in the leaf nodes, that is in elementary and scalar operators. - TEval evaluate(product_operator &op); - - /// @brief Adds two operators that act on the same degrees of freedom. - TEval add(TEval val1, TEval val2); - - /// @brief Multiplies two operators that act on the same degrees of freedom. - TEval mul(TEval val1, TEval val2); - - /// @brief Computes the tensor product of two operators that act on different - /// degrees of freedom. - TEval tensor(TEval val1, TEval val2); -}; - -class EvaluatedMatrix { - friend class MatrixArithmetics; - -private: - std::vector m_degrees; - matrix_2 m_matrix; - -public: - EvaluatedMatrix() = default; - EvaluatedMatrix(std::vector degrees, matrix_2 matrix) - : m_degrees(degrees), m_matrix(matrix) {} - - /// @brief The degrees of freedom that the matrix of the evaluated value - /// applies to. - std::vector degrees() { return m_degrees; } - - /// @brief The matrix representation of an evaluated operator, according - /// to the sequence of degrees of freedom associated with the evaluated - /// value. - matrix_2 matrix() { return m_matrix; } -}; - -/// Encapsulates the functions needed to compute the matrix representation -/// of an operator expression. -class MatrixArithmetics : public OperatorArithmetics { -private: - std::vector _compute_permutation(std::vector op_degrees, - std::vector canon_degrees); - std::tuple> - _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); - -public: - std::map &m_dimensions; // fixme: make const - std::map> - &m_parameters; // fixme: make const - - MatrixArithmetics(std::map dimensions, - std::map> parameters) - : m_dimensions(dimensions), m_parameters(parameters) {} - - // Computes the tensor product of two evaluate operators that act on - // different degrees of freedom using the kronecker product. - EvaluatedMatrix tensor(EvaluatedMatrix op1, EvaluatedMatrix op2); - // Multiplies two evaluated operators that act on the same degrees - // of freedom. - EvaluatedMatrix mul(EvaluatedMatrix op1, EvaluatedMatrix op2); - // Adds two evaluated operators that act on the same degrees - // of freedom. - EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); - // Computes the matrix of an ElementaryOperator or ScalarOperator using its - // `to_matrix` method. - EvaluatedMatrix evaluate(std::variant> - op); -}; - } // namespace cudaq \ No newline at end of file From 138b2e93834a3310b1000f17dd049960401eb7f1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 5 Feb 2025 11:03:26 +0000 Subject: [PATCH 253/311] clean up (moving some files) Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 5 +- runtime/cudaq/dynamics/callback.cpp | 92 +++++++ .../{definition.h => dynamics/callback.h} | 116 +++------ runtime/cudaq/dynamics/definition.cpp | 36 --- runtime/cudaq/dynamics/manipulation.cpp | 31 ++- runtime/cudaq/dynamics/matrix_operators.cpp | 4 +- runtime/cudaq/dynamics/scalar_operators.cpp | 226 +++++++++--------- runtime/cudaq/dynamics/spin_operators.cpp | 8 + runtime/cudaq/operator_utils.h | 40 ---- runtime/cudaq/operators.h | 11 +- 10 files changed, 265 insertions(+), 304 deletions(-) create mode 100644 runtime/cudaq/dynamics/callback.cpp rename runtime/cudaq/{definition.h => dynamics/callback.h} (51%) delete mode 100644 runtime/cudaq/dynamics/definition.cpp create mode 100644 runtime/cudaq/dynamics/spin_operators.cpp delete mode 100644 runtime/cudaq/operator_utils.h diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 7b15e53633..cc4850ab67 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -11,10 +11,9 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC - helpers.cpp - rydberg_hamiltonian.cpp - definition.cpp + callback.cpp scalar_operators.cpp + spin_operators.cpp matrix_operators.cpp product_operators.cpp operator_sum.cpp diff --git a/runtime/cudaq/dynamics/callback.cpp b/runtime/cudaq/dynamics/callback.cpp new file mode 100644 index 0000000000..c04da8752d --- /dev/null +++ b/runtime/cudaq/dynamics/callback.cpp @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "callback.h" + +#include +#include +#include +#include + +namespace cudaq { + +// ScalarCallbackFunction + +ScalarCallbackFunction::ScalarCallbackFunction(const ScalarCallbackFunction &other) { + _callback_func = other._callback_func; +} + +ScalarCallbackFunction::ScalarCallbackFunction(ScalarCallbackFunction &&other) { + _callback_func = std::move(other._callback_func); +} + +ScalarCallbackFunction& ScalarCallbackFunction::operator=(const ScalarCallbackFunction &other) { + if (this != &other) { + _callback_func = other._callback_func; + } + return *this; +} + +ScalarCallbackFunction& ScalarCallbackFunction::operator=(ScalarCallbackFunction &&other) { + if (this != &other) { + _callback_func = std::move(other._callback_func); + } + return *this; +} + +std::complex +ScalarCallbackFunction::operator()(std::map> parameters) const { + return _callback_func(std::move(parameters)); +} + +// MatrixCallbackFunction + +MatrixCallbackFunction::MatrixCallbackFunction(const MatrixCallbackFunction &other) { + _callback_func = other._callback_func; +} + +MatrixCallbackFunction::MatrixCallbackFunction(MatrixCallbackFunction &&other) { + _callback_func = std::move(other._callback_func); +} + +MatrixCallbackFunction& MatrixCallbackFunction::operator=(const MatrixCallbackFunction &other) { + if (this != &other) { + _callback_func = other._callback_func; + } + return *this; +} + +MatrixCallbackFunction& MatrixCallbackFunction::operator=(MatrixCallbackFunction &&other) { + if (this != &other) { + _callback_func = std::move(other._callback_func); + } + return *this; +} + +matrix_2 +MatrixCallbackFunction::operator()(std::vector relevant_dimensions, + std::map> parameters) const { + return _callback_func(std::move(relevant_dimensions), std::move(parameters)); +} + +// Definition + +Definition::Definition(const std::string &operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create) + : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} + +Definition::Definition(Definition &&def) + : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} + +matrix_2 Definition::generate_matrix( + const std::vector &relevant_dimensions, + const std::map> ¶meters) const { + return generator(relevant_dimensions, parameters); +} + +Definition::~Definition() = default; +} // namespace cudaq diff --git a/runtime/cudaq/definition.h b/runtime/cudaq/dynamics/callback.h similarity index 51% rename from runtime/cudaq/definition.h rename to runtime/cudaq/dynamics/callback.h index 49decefeb6..d21e412c56 100644 --- a/runtime/cudaq/definition.h +++ b/runtime/cudaq/dynamics/callback.h @@ -20,136 +20,88 @@ namespace cudaq { -using Func = std::function, std::map>)>; - -class CallbackFunction { +class ScalarCallbackFunction { private: - // The user provided callback function that takes a vector defining the - // dimension for each degree of freedom it acts on, and a map of complex + // The user provided callback function that takes a map of complex // parameters. - Func _callback_func; + std::function(std::map>)> _callback_func; public: - CallbackFunction() = default; - template - CallbackFunction(Callable &&callable) { + ScalarCallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v, + std::is_invocable_r_v, Callable, std::map>>, - "Invalid callback function. Must have signature " - "matrix_2(" - "std::map, " + "Invalid callback function. Must have signature std::complex(" "std::map>)"); _callback_func = std::forward(callable); } // copy constructor - CallbackFunction(const CallbackFunction &other) { - _callback_func = other._callback_func; - } + ScalarCallbackFunction(const ScalarCallbackFunction &other); // move constructor. - CallbackFunction(CallbackFunction &&other) { - _callback_func = std::move(other._callback_func); - } + ScalarCallbackFunction(ScalarCallbackFunction &&other); // assignment operator - CallbackFunction &operator=(const CallbackFunction &other) { - if (this != &other) { - _callback_func = other._callback_func; - } - return *this; - } + ScalarCallbackFunction& operator=(const ScalarCallbackFunction &other); // move assignment operator - CallbackFunction &operator=(CallbackFunction &&other) { - if (this != &other) { - _callback_func = std::move(other._callback_func); - } - return *this; - } + ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other); - bool operator!() { return (!_callback_func); } - - matrix_2 - operator()(std::vector relevant_dimensions, - std::map> parameters) const { - return _callback_func(std::move(relevant_dimensions), - std::move(parameters)); - } + std::complex + operator()(std::map> parameters) const; }; -using ScalarFunc = std::function( - std::map>)>; -// A scalar callback function does not need to accept the dimensions, -// therefore we will use a different function type for this specific class. -class ScalarCallbackFunction : CallbackFunction { +class MatrixCallbackFunction { private: - // The user provided callback function that takes a vector of parameters. - ScalarFunc _callback_func; + // The user provided callback function that takes a vector defining the + // dimension for each degree of freedom it acts on, and a map of complex + // parameters. + std::function, std::map>)> _callback_func; public: - ScalarCallbackFunction() = default; - template - ScalarCallbackFunction(Callable &&callable) { + MatrixCallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v, Callable, + std::is_invocable_r_v, std::map>>, - "Invalid callback function. Must have signature std::complex(" + "Invalid callback function. Must have signature " + "matrix_2(" + "std::map, " "std::map>)"); _callback_func = std::forward(callable); } // copy constructor - ScalarCallbackFunction(const ScalarCallbackFunction &other) { - _callback_func = other._callback_func; - } + MatrixCallbackFunction(const MatrixCallbackFunction &other); // move constructor. - ScalarCallbackFunction(ScalarCallbackFunction &&other) { - _callback_func = std::move(other._callback_func); - } + MatrixCallbackFunction(MatrixCallbackFunction &&other); // assignment operator - ScalarCallbackFunction &operator=(const ScalarCallbackFunction &other) { - if (this != &other) { - _callback_func = other._callback_func; - } - return *this; - } + MatrixCallbackFunction& operator=(const MatrixCallbackFunction &other); // move assignment operator - ScalarCallbackFunction &operator=(ScalarCallbackFunction &&other) { - if (this != &other) { - _callback_func = std::move(other._callback_func); - } - return *this; - } - - bool operator!() { return (!_callback_func); } + MatrixCallbackFunction& operator=(MatrixCallbackFunction &&other); - std::complex - operator()(std::map> parameters) const { - return _callback_func(std::move(parameters)); - } + matrix_2 + operator()(std::vector relevant_dimensions, + std::map> parameters) const; }; -/// @brief Object used to give an error if a Definition of an elementary -/// or scalar operator is instantiated by other means than the `define` -/// class method. + +/// @brief Object used to store the definition of a custom matrix operator. class Definition { private: std::string id; - CallbackFunction generator; + MatrixCallbackFunction generator; std::vector m_expected_dimensions; public: - Definition(const std::string &operator_id, - std::vector expected_dimensions, CallbackFunction &&create); + + Definition(const std::string &operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create); Definition(Definition &&def); ~Definition(); diff --git a/runtime/cudaq/dynamics/definition.cpp b/runtime/cudaq/dynamics/definition.cpp deleted file mode 100644 index 61e83d0a52..0000000000 --- a/runtime/cudaq/dynamics/definition.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/definition.h" -#include "cudaq/qis/state.h" - -#include -#include -#include -#include - -namespace cudaq { - -Definition::Definition(const std::string &operator_id, - std::vector expected_dimensions, - CallbackFunction &&create) - : id(operator_id), generator(std::move(create)), - m_expected_dimensions(std::move(expected_dimensions)) {} - -Definition::Definition(Definition &&def) - : id(def.id), generator(std::move(def.generator)), - m_expected_dimensions(std::move(def.m_expected_dimensions)) {} - -matrix_2 Definition::generate_matrix( - const std::vector &relevant_dimensions, - const std::map> ¶meters) const { - return generator(relevant_dimensions, parameters); -} - -Definition::~Definition() = default; -} // namespace cudaq diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index a8252f51c6..959294c85d 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -6,13 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -<<<<<<< HEAD -#include "cudaq/helpers.h" -#include "cudaq/operators.h" -======= #include "manipulation.h" #include "helpers.h" ->>>>>>> 558b2496f (general clean up) +#include namespace cudaq { @@ -27,7 +23,14 @@ const matrix_2& EvaluatedMatrix::matrix() const { } EvaluatedMatrix::EvaluatedMatrix(const std::vector °rees, const matrix_2 &matrix) - : targets(degrees), value(matrix) {} + : targets(degrees), value(matrix) { +#if !defined(NDEBUG) + std::set unique_degrees; + for (auto d : degrees) + unique_degrees.insert(d); + assert(unique_degrees.size() == degrees.size()); +#endif + } EvaluatedMatrix::EvaluatedMatrix(EvaluatedMatrix &&other) : targets(std::move(other.targets)), value(std::move(other.value)) {} @@ -92,16 +95,14 @@ MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, EvaluatedMatrix op2) { - /// FIXME: do this check: - // assert len(frozenset(op1.degrees).intersection(op2.degrees)) == 0, \ - // "Operators should not have common degrees of freedom." - std::vector op_degrees; op_degrees.reserve(op1.degrees().size() + op2.degrees().size()); for (auto d : op1.degrees()) op_degrees.push_back(d); - for (auto d : op2.degrees()) + for (auto d : op2.degrees()) { + assert(std::find(op_degrees.begin(), op_degrees.end(), d) == op_degrees.end()); op_degrees.push_back(d); + } auto op_matrix = cudaq::kronecker(op1.matrix(), op2.matrix()); auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); return EvaluatedMatrix(new_degrees, new_matrix); @@ -113,9 +114,7 @@ EvaluatedMatrix MatrixArithmetics::mul(EvaluatedMatrix op1, // convention for how to define the matrix. Tensor products permute the // computed matrix if necessary to guarantee that all operators always have // sorted degrees. - if (op1.degrees() != op2.degrees()) - throw std::runtime_error( - "Operators should have the same order of degrees."); + assert(op1.degrees() == op2.degrees()); return EvaluatedMatrix(op1.degrees(), (op1.matrix() * op2.matrix())); } @@ -125,9 +124,7 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, // convention for how to define the matrix. Tensor products permute the // computed matrix if necessary to guarantee that all operators always have // sorted degrees. - if (op1.degrees() != op2.degrees()) - throw std::runtime_error( - "Operators should have the same order of degrees."); + assert(op1.degrees() == op2.degrees()); return EvaluatedMatrix(op1.degrees(), (op1.matrix() + op2.matrix())); } diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 574028be35..dfc9523bfc 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -19,8 +19,8 @@ namespace cudaq { std::map matrix_operator::m_ops = {}; void matrix_operator::define(std::string operator_id, std::vector expected_dimensions, - CallbackFunction &&create) { - auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); + MatrixCallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { throw std::runtime_error("an matrix operator with name " + operator_id + "is already defined"); diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index a7f2f052c9..d4540e140f 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -15,43 +15,35 @@ namespace cudaq { // constructors and destructors -scalar_operator::scalar_operator(double value) - : constant_value(value), generator() {} +scalar_operator::scalar_operator(double value) + : value(std::variant, ScalarCallbackFunction>(std::complex(value))) {} -scalar_operator::scalar_operator(std::complex value) - : constant_value(value), generator() {} +scalar_operator::scalar_operator(std::complex value) + : value(std::variant, ScalarCallbackFunction>(value)) {} -scalar_operator::scalar_operator(const ScalarCallbackFunction &create) - : constant_value(), generator(create) {} +scalar_operator::scalar_operator(const ScalarCallbackFunction &create) + : value(std::variant, ScalarCallbackFunction>(create)) {} scalar_operator::scalar_operator(ScalarCallbackFunction &&create) - : constant_value() { - generator = std::move(create); -} + : value(std::variant, ScalarCallbackFunction>(std::move(create))) {} -scalar_operator::scalar_operator(const scalar_operator &other) - : constant_value(other.constant_value), generator(other.generator) {} +scalar_operator::scalar_operator(const scalar_operator &other) + : value(other.value) {} -scalar_operator::scalar_operator(scalar_operator &&other) - : constant_value(other.constant_value) { - generator = std::move(other.generator); -} +scalar_operator::scalar_operator(scalar_operator &&other) + : value(std::move(other.value)) {} // assignments -scalar_operator &scalar_operator::operator=(const scalar_operator &other) { - if (this != &other) { - constant_value = other.constant_value; - generator = other.generator; - } +scalar_operator& scalar_operator::operator=(const scalar_operator &other) { + if (this != &other) + this->value = other.value; return *this; } -scalar_operator &scalar_operator::operator=(scalar_operator &&other) { - if (this != &other) { - constant_value = other.constant_value; - generator = std::move(other.generator); - } +scalar_operator& scalar_operator::operator=(scalar_operator &&other) { + if (this != &other) + this->value = std::move(other.value); return *this; } @@ -59,14 +51,9 @@ scalar_operator &scalar_operator::operator=(scalar_operator &&other) { std::complex scalar_operator::evaluate( const std::map> parameters) const { - if (constant_value.has_value()) - return constant_value.value(); - else - return generator(parameters); -} - -ScalarCallbackFunction scalar_operator::get_generator() const { - return generator; + if (std::holds_alternative(this->value)) + return std::get(this->value)(parameters); + return std::get>(this->value); } matrix_2 scalar_operator::to_matrix( @@ -80,10 +67,12 @@ matrix_2 scalar_operator::to_matrix( // comparison bool scalar_operator::operator==(scalar_operator other) { - if (this->constant_value.has_value() && other.constant_value.has_value()) { - return this->constant_value == other.constant_value; + if (std::holds_alternative(this->value)) { + return std::holds_alternative(other.value) && + &std::get(this->value) == &std::get(other.value); } else { - throw std::runtime_error("not implemented"); + return std::holds_alternative>(this->value) && + std::get>(this->value) == std::get>(other.value); } } @@ -95,17 +84,18 @@ scalar_operator scalar_operator::operator+() const { return *this; } // right-hand arithmetics -#define ARITHMETIC_OPERATIONS(op, otherTy) \ - scalar_operator scalar_operator::operator op(otherTy other) const { \ - if (this->constant_value.has_value()) { \ - return scalar_operator(this->constant_value.value() op other); \ - } \ - auto newGenerator = \ - [other, generator = this->generator]( \ - std::map> parameters) { \ - return generator(parameters) op other; \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS(op, otherTy) \ + scalar_operator scalar_operator::operator op(otherTy other) const { \ + if (std::holds_alternative>(this->value)) { \ + return scalar_operator( \ + std::get>(this->value) op other); \ + } \ + auto newGenerator = \ + [other, generator = std::get(this->value)]( \ + std::map> parameters) { \ + return generator(parameters) op other; \ + }; \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS(*, double); @@ -117,20 +107,21 @@ ARITHMETIC_OPERATIONS(/, std::complex); ARITHMETIC_OPERATIONS(+, std::complex); ARITHMETIC_OPERATIONS(-, std::complex); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op(const scalar_operator &other) \ - const { \ - if (this->constant_value.has_value() && \ - other.constant_value.has_value()) { \ - auto res = this->constant_value.value() op other.constant_value.value(); \ - return scalar_operator(res); \ - } \ - auto newGenerator = \ - [other, \ - *this](std::map> parameters) { \ - return this->evaluate(parameters) op other.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator scalar_operator::operator op( \ + const scalar_operator &other) const { \ + if (std::holds_alternative>(this->value) && \ + std::holds_alternative>(other.value)) { \ + return scalar_operator( \ + std::get>(this->value) op \ + std::get>(other.value)); \ + } \ + auto newGenerator = \ + [other, *this]( \ + std::map> parameters) { \ + return this->evaluate(parameters) op other.evaluate(parameters); \ + }; \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_SCALAR_OPS(*); @@ -138,55 +129,57 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(/); ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ - scalar_operator &scalar_operator::operator op(otherTy other) { \ - if (this->constant_value.has_value()) { \ - this->constant_value.value() op other; \ - return *this; \ - } \ - auto newGenerator = \ - [other, generator = std::move(this->generator)]( \ - std::map> parameters) { \ - return generator(parameters) op other; \ - }; \ - this->generator = newGenerator; \ - return *this; \ +#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ + scalar_operator& scalar_operator::operator op##=(otherTy other) { \ + if (std::holds_alternative>(this->value)) { \ + this->value = std::get>(this->value) op other; \ + return *this; \ + } \ + auto newGenerator = \ + [other, generator = std::move(std::get(this->value))]( \ + std::map> parameters) { \ + return generator(parameters) op##= other; \ + }; \ + this->value = newGenerator; \ + return *this; \ } -ARITHMETIC_OPERATIONS_ASSIGNMENT(*=, double); -ARITHMETIC_OPERATIONS_ASSIGNMENT(/=, double); -ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, double); -ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, double); -ARITHMETIC_OPERATIONS_ASSIGNMENT(*=, std::complex); -ARITHMETIC_OPERATIONS_ASSIGNMENT(/=, std::complex); -ARITHMETIC_OPERATIONS_ASSIGNMENT(+=, std::complex); -ARITHMETIC_OPERATIONS_ASSIGNMENT(-=, std::complex); - -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - scalar_operator &scalar_operator::operator op( \ - const scalar_operator &other) { \ - if (this->constant_value.has_value() && \ - other.constant_value.has_value()) { \ - this->constant_value.value() op other.constant_value.value(); \ - return *this; \ - } \ - auto newGenerator = \ - [other, \ - *this](std::map> parameters) { \ - return this->evaluate(parameters) op other.evaluate(parameters); \ - }; \ - this->generator = newGenerator; \ - return *this; \ +ARITHMETIC_OPERATIONS_ASSIGNMENT(*, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(/, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(+, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(-, double); +ARITHMETIC_OPERATIONS_ASSIGNMENT(*, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(/, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(+, std::complex); +ARITHMETIC_OPERATIONS_ASSIGNMENT(-, std::complex); + +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + scalar_operator& scalar_operator::operator op##=( \ + const scalar_operator &other) { \ + if (std::holds_alternative>(this->value) && \ + std::holds_alternative>(other.value)) { \ + this->value = \ + std::get>(this->value) op \ + std::get>(other.value); \ + return *this; \ + } \ + auto newGenerator = \ + [other, *this]( \ + std::map> parameters) { \ + return this->evaluate(parameters) op##= other.evaluate(parameters); \ + }; \ + this->value = newGenerator; \ + return *this; \ } -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+=); -ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-=); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+); +ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-); -#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ - scalar_operator operator op(scalar_operator &&self, otherTy other) { \ - return std::move(self op## = other); \ +#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ + scalar_operator operator op(scalar_operator &&self, otherTy other) { \ + return std::move(self op##= other); \ } ARITHMETIC_OPERATIONS_RVALUE(*, double); @@ -200,17 +193,18 @@ ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); // left-hand arithmetics -#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ - scalar_operator operator op(otherTy other, const scalar_operator &self) { \ - if (self.constant_value.has_value()) { \ - return scalar_operator(other op self.constant_value.value()); \ - } \ - auto newGenerator = \ - [other, generator = self.generator]( \ - std::map> parameters) { \ - return other op generator(parameters); \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ + scalar_operator operator op(otherTy other, const scalar_operator &self) { \ + if (std::holds_alternative>(self.value)) { \ + return scalar_operator( \ + other op std::get>(self.value)); \ + } \ + auto newGenerator = \ + [other, generator = std::get(self.value)]( \ + std::map> parameters) { \ + return other op generator(parameters); \ + }; \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_REVERSE(*, double); diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp new file mode 100644 index 0000000000..a1cd462520 --- /dev/null +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -0,0 +1,8 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + diff --git a/runtime/cudaq/operator_utils.h b/runtime/cudaq/operator_utils.h deleted file mode 100644 index 568cc8298e..0000000000 --- a/runtime/cudaq/operator_utils.h +++ /dev/null @@ -1,40 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "definition.h" -#include "matrix.h" -#include -#include -#include -#include -#include -#include - -namespace cudaq { - -inline std::map> -aggregate_parameters(const std::map ¶m1, - const std::map ¶m2) { - std::map> merged_map = param1; - - for (const auto &[key, value] : param2) { - /// FIXME: May just be able to remove this whole conditional block - /// since we're not dealing with std::string entries, but instead - /// complex doubles now. - if (merged_map.find(key) != merged_map.end()) { - // do nothing - } else { - merged_map[key] = value; - } - } - - return merged_map; -} -} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index a603f14b31..854175e7ed 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,8 +8,8 @@ #pragma once -#include "definition.h" #include "dynamics/templates.h" +#include "dynamics/callback.h" #include "utils/tensor.h" #include @@ -28,12 +28,7 @@ class scalar_operator { private: // If someone gave us a constant value, we will just return that // directly to them when they call `evaluate`. - std::optional> constant_value; - - /// @brief The function that generates the value of the scalar operator. - /// The function can take a vector of complex-valued arguments - /// and returns a number. - ScalarCallbackFunction generator; + std::variant, ScalarCallbackFunction> value; public: // constructors and destructors @@ -545,7 +540,7 @@ class matrix_operator { /// short), if the operator acts /// on multiple degrees of freedom. static void define(std::string operator_id, std::vector expected_dimensions, - CallbackFunction &&create); + MatrixCallbackFunction &&create); // read-only properties From 2669d957e299de62143d5a7d288079a7d71178e6 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 5 Feb 2025 15:52:34 +0000 Subject: [PATCH 254/311] prep for adding different handlers Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/matrix_operators.cpp | 1 + runtime/cudaq/dynamics/matrix_operators.h | 131 ++++++ runtime/cudaq/dynamics/operator_sum.cpp | 379 ++++++++++-------- runtime/cudaq/dynamics/product_operators.cpp | 354 ++++++++-------- runtime/cudaq/dynamics/templates.h | 123 +++--- runtime/cudaq/operators.h | 103 +---- unittests/dynamics/matrix_ops_arithmetic.cpp | 1 + unittests/dynamics/matrix_ops_simple.cpp | 1 + unittests/dynamics/operator_sum.cpp | 1 + .../dynamics/product_operators_arithmetic.cpp | 1 + 10 files changed, 602 insertions(+), 493 deletions(-) create mode 100644 runtime/cudaq/dynamics/matrix_operators.h diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index dfc9523bfc..6d6bcc8565 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "matrix_operators.h" #include #include diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h new file mode 100644 index 0000000000..8489cf39b1 --- /dev/null +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -0,0 +1,131 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include "cudaq/operators.h" + +namespace cudaq { + +class matrix_operator : operator_handler{ + +private: + + static std::map m_ops; + + std::vector targets; + std::string id; + +public: + + // tools for custom operators + + /// @brief Adds the definition of an elementary operator with the given id to + /// the class. After definition, an the defined elementary operator can be + /// instantiated by providing the operator id as well as the degree(s) of + /// freedom that it acts on. An elementary operator is a parameterized object + /// acting on certain degrees of freedom. To evaluate an operator, for example + /// to compute its matrix, the level, that is the dimension, for each degree + /// of freedom it acts on must be provided, as well as all additional + /// parameters. Additional parameters must be provided in the form of keyword + /// arguments. Note: The dimensions passed during operator evaluation are + /// automatically validated against the expected dimensions specified during + /// definition - the `create` function does not need to do this. + /// @arg operator_id : A string that uniquely identifies the defined operator. + /// @arg expected_dimensions : Defines the number of levels, that is the + /// dimension, + /// for each degree of freedom in canonical (that is sorted) order. A + /// negative or zero value for one (or more) of the expected dimensions + /// indicates that the operator is defined for any dimension of the + /// corresponding degree of freedom. + /// @arg create : Takes any number of complex-valued arguments and returns the + /// matrix representing the operator in canonical order. If the matrix + /// can be defined for any number of levels for one or more degree of + /// freedom, the `create` function must take an argument called + /// `dimension` (or `dim` for short), if the operator acts on a single + /// degree of freedom, and an argument called `dimensions` (or `dims` for + /// short), if the operator acts + /// on multiple degrees of freedom. + static void define(std::string operator_id, std::vector expected_dimensions, + MatrixCallbackFunction &&create); + + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + virtual const std::vector& degrees() const; + + // constructors and destructors + + // The constructor should never be called directly by the user: + // Keeping it internally documented for now, however. + /// @brief Constructor. + /// @arg operator_id : The ID of the operator as specified when it was + /// defined. + /// @arg degrees : the degrees of freedom that the operator acts upon. + matrix_operator(std::string operator_id, const std::vector °rees); + + // constructor + matrix_operator(std::string operator_id, std::vector &°rees); + + // copy constructor + matrix_operator(const matrix_operator &other); + + // move constructor + matrix_operator(matrix_operator &&other); + + ~matrix_operator() = default; + + // assignments + + // assignment operator + matrix_operator& operator=(const matrix_operator& other); + + // move assignment operator + matrix_operator& operator=(matrix_operator &&other); + + // evaluations + + /// @brief Return the `matrix_operator` as a matrix. + /// @arg `dimensions` : A map specifying the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0 : 2, 1 : 2}`. + virtual matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; + + // comparisons + + /// @brief True, if the other value is an elementary operator with the same id + /// acting on the same degrees of freedom, and False otherwise. + bool operator==(const matrix_operator &other) const; + + // predefined operators + + static product_operator identity(int degree); + static product_operator zero(int degree); + static product_operator annihilate(int degree); + static product_operator create(int degree); + static product_operator momentum(int degree); + static product_operator number(int degree); + static product_operator parity(int degree); + static product_operator position(int degree); + /// Operators that accept parameters at runtime. + static product_operator squeeze(int degree); + static product_operator displace(int degree); +}; + +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +template class product_operator; +template class operator_sum; +#else +extern template class product_operator; +extern template class operator_sum; +#endif + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index cc476da3a5..16e0294587 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -10,6 +10,7 @@ #include "cudaq/operators.h" #include "helpers.h" #include "manipulation.h" +#include "matrix_operators.h" #include #include @@ -75,20 +76,24 @@ void operator_sum::aggregate_terms( aggregate_terms(std::forward(args)...); } -template cudaq::matrix_2 -operator_sum::m_evaluate(MatrixArithmetics arithmetics, - bool pad_terms) const; - -// no overload for a single product, since we don't want a constructor for a single term - -template void operator_sum::aggregate_terms( - const product_operator &item1, - const product_operator &item2); - -template void operator_sum::aggregate_terms( - const product_operator &item1, - const product_operator &item2, - const product_operator &item3); +#define INSTANTIATE_SUM_PRIVATE_METHODS(HandlerTy) \ + \ + template \ + cudaq::matrix_2 operator_sum::m_evaluate( \ + MatrixArithmetics arithmetics, bool pad_terms) const; \ + \ + /* no overload for a single product, since we don't want a constructor for a single term */ \ + \ + template \ + void operator_sum::aggregate_terms(const product_operator &item1, \ + const product_operator &item2); \ + \ + template \ + void operator_sum::aggregate_terms(const product_operator &item1, \ + const product_operator &item2, \ + const product_operator &item3); + +INSTANTIATE_SUM_PRIVATE_METHODS(matrix_operator); // read-only properties @@ -120,13 +125,18 @@ operator_sum::get_terms() const { } return prods; } - -template std::vector operator_sum::degrees() const; - -template int operator_sum::n_terms() const; - -template std::vector> -operator_sum::get_terms() const; +#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ + \ + template \ + std::vector operator_sum::degrees() const; \ + \ + template \ + int operator_sum::n_terms() const; \ + \ + template \ + std::vector> operator_sum::get_terms() const; + +INSTANTIATE_SUM_PROPERTIES(matrix_operator); // constructors @@ -168,29 +178,32 @@ operator_sum::operator_sum(operator_sum &&other) : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} -// no constructor for a single product, since that one should remain a product -// op - -template operator_sum::operator_sum( - const product_operator &item1, - const product_operator &item2); - -template operator_sum::operator_sum( - const product_operator &item1, - const product_operator &item2, - const product_operator &item3); - -template operator_sum::operator_sum( - const std::vector> &terms); - -template operator_sum::operator_sum( - std::vector> &&terms); - -template operator_sum::operator_sum( - const operator_sum &other); - -template operator_sum::operator_sum( - operator_sum &&other); +#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ + \ + /* no constructor for a single product, since that one should remain a product op */ \ + \ + template \ + operator_sum::operator_sum(const product_operator &item1, \ + const product_operator &item2); \ + \ + template \ + operator_sum::operator_sum(const product_operator &item1, \ + const product_operator &item2, \ + const product_operator &item3); \ + \ + template \ + operator_sum::operator_sum(const std::vector> &terms); \ + \ + template \ + operator_sum::operator_sum(std::vector> &&terms); \ + \ + template \ + operator_sum::operator_sum(const operator_sum &other); \ + \ + template \ + operator_sum::operator_sum(operator_sum &&other); + +INSTANTIATE_SUM_CONSTRUCTORS(matrix_operator); // assignments @@ -214,12 +227,17 @@ operator_sum::operator=(operator_sum &&other) { return *this; } -template operator_sum & -operator_sum::operator=( - const operator_sum &other); +#define INSTANTIATE_SUM_ASSIGNMENTS(HandlerTy) \ + \ + template \ + operator_sum& operator_sum::operator=( \ + const operator_sum& other); \ + \ + template \ + operator_sum& operator_sum::operator=( \ + operator_sum &&other); -template operator_sum & -operator_sum::operator=(operator_sum &&other); +INSTANTIATE_SUM_ASSIGNMENTS(matrix_operator); // evaluations @@ -235,12 +253,18 @@ matrix_2 operator_sum::to_matrix( return m_evaluate(MatrixArithmetics(dimensions, parameters)); } -template std::string operator_sum::to_string() const; - -template matrix_2 operator_sum::to_matrix( - const std::map &dimensions, +#define INSTANTIATE_SUM_EVALUATIONS(HandlerTy) \ + \ + template \ + std::string operator_sum::to_string() const; \ + \ + template \ + matrix_2 operator_sum::to_matrix( \ + const std::map &dimensions, \ const std::map> ¶ms) const; +INSTANTIATE_SUM_EVALUATIONS(matrix_operator); + // comparisons template @@ -249,8 +273,12 @@ bool operator_sum::operator==( throw std::runtime_error("not implemented"); } -template bool operator_sum::operator==( - const operator_sum &other) const; +#define INSTANTIATE_SUM_COMPARISONS(HandlerTy) \ + \ + template \ + bool operator_sum::operator==(const operator_sum &other) const; + +INSTANTIATE_SUM_COMPARISONS(matrix_operator); // unary operators @@ -271,11 +299,15 @@ operator_sum operator_sum::operator+() const { return *this; } -template operator_sum -operator_sum::operator-() const; +#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ + \ + template \ + operator_sum operator_sum::operator-() const; \ + \ + template \ + operator_sum operator_sum::operator+() const; -template operator_sum -operator_sum::operator+() const; +INSTANTIATE_SUM_UNARY_OPS(matrix_operator); // right-hand arithmetics @@ -368,30 +400,34 @@ operator_sum::operator*(const HandlerTy &other) const { SUM_ADDITION_HANDLER(+) SUM_ADDITION_HANDLER(-) -template operator_sum -operator_sum::operator*(double other) const; -template operator_sum -operator_sum::operator+(double other) const; -template operator_sum -operator_sum::operator-(double other) const; -template operator_sum -operator_sum::operator*(std::complex other) const; -template operator_sum -operator_sum::operator+(std::complex other) const; -template operator_sum -operator_sum::operator-(std::complex other) const; -template operator_sum -operator_sum::operator*(const scalar_operator &other) const; -template operator_sum -operator_sum::operator+(const scalar_operator &other) const; -template operator_sum -operator_sum::operator-(const scalar_operator &other) const; -template operator_sum -operator_sum::operator*(const matrix_operator &other) const; -template operator_sum -operator_sum::operator+(const matrix_operator &other) const; -template operator_sum -operator_sum::operator-(const matrix_operator &other) const; +#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ + \ + template \ + operator_sum operator_sum::operator*(double other) const; \ + template \ + operator_sum operator_sum::operator+(double other) const; \ + template \ + operator_sum operator_sum::operator-(double other) const; \ + template \ + operator_sum operator_sum::operator*(std::complex other) const; \ + template \ + operator_sum operator_sum::operator+(std::complex other) const; \ + template \ + operator_sum operator_sum::operator-(std::complex other) const; \ + template \ + operator_sum operator_sum::operator*(const scalar_operator &other) const; \ + template \ + operator_sum operator_sum::operator+(const scalar_operator &other) const; \ + template \ + operator_sum operator_sum::operator-(const scalar_operator &other) const; \ + template \ + operator_sum operator_sum::operator*(const HandlerTy &other) const; \ + template \ + operator_sum operator_sum::operator+(const HandlerTy &other) const; \ + template \ + operator_sum operator_sum::operator-(const HandlerTy &other) const; + +INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); template operator_sum operator_sum::operator*( @@ -494,18 +530,28 @@ operator_sum::operator*(const operator_sum &other) const { SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); -template operator_sum operator_sum::operator*( - const product_operator &other) const; -template operator_sum operator_sum::operator+( - const product_operator &other) const; -template operator_sum operator_sum::operator-( - const product_operator &other) const; -template operator_sum operator_sum::operator*( - const operator_sum &other) const; -template operator_sum operator_sum::operator+( - const operator_sum &other) const; -template operator_sum operator_sum::operator-( - const operator_sum &other) const; +#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + operator_sum operator_sum::operator*( \ + const product_operator &other) const; \ + template \ + operator_sum operator_sum::operator+( \ + const product_operator &other) const; \ + template \ + operator_sum operator_sum::operator-( \ + const product_operator &other) const; \ + template \ + operator_sum operator_sum::operator*( \ + const operator_sum &other) const; \ + template \ + operator_sum operator_sum::operator+( \ + const operator_sum &other) const; \ + template \ + operator_sum operator_sum::operator-( \ + const operator_sum &other) const; + +INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); #define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -610,48 +656,46 @@ operator_sum::operator*=(const operator_sum &other) { SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); -template operator_sum & -operator_sum::operator*=(double other); -template operator_sum & -operator_sum::operator+=(double other); -template operator_sum & -operator_sum::operator-=(double other); -template operator_sum & -operator_sum::operator*=(std::complex other); -template operator_sum & -operator_sum::operator+=(std::complex other); -template operator_sum & -operator_sum::operator-=(std::complex other); -template operator_sum & -operator_sum::operator*=(const scalar_operator &other); -template operator_sum & -operator_sum::operator+=(const scalar_operator &other); -template operator_sum & -operator_sum::operator-=(const scalar_operator &other); -template operator_sum & -operator_sum::operator*=(const matrix_operator &other); -template operator_sum & -operator_sum::operator+=(const matrix_operator &other); -template operator_sum & -operator_sum::operator-=(const matrix_operator &other); -template operator_sum & -operator_sum::operator*=( - const product_operator &other); -template operator_sum & -operator_sum::operator+=( - const product_operator &other); -template operator_sum & -operator_sum::operator-=( - const product_operator &other); -template operator_sum & -operator_sum::operator*=( - const operator_sum &other); -template operator_sum & -operator_sum::operator-=( - const operator_sum &other); -template operator_sum & -operator_sum::operator+=( - const operator_sum &other); +#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ + \ + template \ + operator_sum& operator_sum::operator*=(double other); \ + template \ + operator_sum& operator_sum::operator+=(double other); \ + template \ + operator_sum& operator_sum::operator-=(double other); \ + template \ + operator_sum& operator_sum::operator*=(std::complex other); \ + template \ + operator_sum& operator_sum::operator+=(std::complex other); \ + template \ + operator_sum& operator_sum::operator-=(std::complex other); \ + template \ + operator_sum& operator_sum::operator*=(const scalar_operator &other); \ + template \ + operator_sum& operator_sum::operator+=(const scalar_operator &other); \ + template \ + operator_sum& operator_sum::operator-=(const scalar_operator &other); \ + template \ + operator_sum& operator_sum::operator*=(const HandlerTy &other); \ + template \ + operator_sum& operator_sum::operator+=(const HandlerTy &other); \ + template \ + operator_sum& operator_sum::operator-=(const HandlerTy &other); \ + template \ + operator_sum& operator_sum::operator*=(const product_operator &other); \ + template \ + operator_sum& operator_sum::operator+=(const product_operator &other); \ + template \ + operator_sum& operator_sum::operator-=(const product_operator &other); \ + template \ + operator_sum& operator_sum::operator*=(const operator_sum &other); \ + template \ + operator_sum& operator_sum::operator-=(const operator_sum &other); \ + template \ + operator_sum& operator_sum::operator+=(const operator_sum &other); + +INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); // left-hand arithmetics @@ -744,38 +788,33 @@ operator_sum operator*(const HandlerTy &other, SUM_ADDITION_HANDLER_REVERSE(+) SUM_ADDITION_HANDLER_REVERSE(-) -template operator_sum -operator*(const scalar_operator &other, - const operator_sum &self); -template operator_sum -operator*(std::complex other, - const operator_sum &self); -template operator_sum -operator*(double other, const operator_sum &self); -template operator_sum -operator*(const matrix_operator &other, - const operator_sum &self); -template operator_sum -operator+(const scalar_operator &other, - const operator_sum &self); -template operator_sum -operator+(double other, const operator_sum &self); -template operator_sum -operator+(std::complex other, - const operator_sum &self); -template operator_sum -operator+(const matrix_operator &other, - const operator_sum &self); -template operator_sum -operator-(const scalar_operator &other, - const operator_sum &self); -template operator_sum -operator-(double other, const operator_sum &self); -template operator_sum -operator-(std::complex other, - const operator_sum &self); -template operator_sum -operator-(const matrix_operator &other, - const operator_sum &self); +#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator*(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator*(double other, const operator_sum &self); \ + template \ + operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ + template \ + operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator+(double other, const operator_sum &self); \ + template \ + operator_sum operator+(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ + template \ + operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator-(double other, const operator_sum &self); \ + template \ + operator_sum operator-(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator-(const HandlerTy &other, const operator_sum &self); + +INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index a7a7c218ce..c75f542f7b 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -10,6 +10,7 @@ #include "cudaq/operators.h" #include "helpers.h" #include "manipulation.h" +#include "matrix_operators.h" #include #include @@ -40,7 +41,7 @@ product_operator::m_evaluate(MatrixArithmetics arithmetics, auto degrees = this->degrees(); cudaq::matrix_2 result; - auto padded_op = [](MatrixArithmetics &arithmetics, const cudaq::matrix_operator &op, + auto padded_op = [](MatrixArithmetics &arithmetics, const HandlerTy &op, std::vector degrees, std::map dimensions, std::map> parameters) { std::vector padded; @@ -87,17 +88,22 @@ product_operator::m_evaluate(MatrixArithmetics arithmetics, return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } -template -void product_operator::aggregate_terms(const matrix_operator &item1, - const matrix_operator &item2); - -template void product_operator::aggregate_terms( - const matrix_operator &item1, const matrix_operator &item2, - const matrix_operator &item3); - -template -cudaq::matrix_2 product_operator::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const; +#define INSTANTIATE_PRODUCT_PRIVATE_METHODS(HandlerTy) \ + \ + template \ + void product_operator::aggregate_terms(const HandlerTy &item1, \ + const HandlerTy &item2); \ + \ + template \ + void product_operator::aggregate_terms(const HandlerTy &item1, \ + const HandlerTy &item2, \ + const HandlerTy &item3); \ + \ + template \ + cudaq::matrix_2 product_operator::m_evaluate( \ + MatrixArithmetics arithmetics, bool pad_terms) const; + +INSTANTIATE_PRODUCT_PRIVATE_METHODS(matrix_operator); // read-only properties @@ -127,16 +133,21 @@ scalar_operator product_operator::get_coefficient() const { return this->coefficients[0]; } -template -std::vector product_operator::degrees() const; - -template int product_operator::n_terms() const; - -template std::vector -product_operator::get_terms() const; - -template scalar_operator -product_operator::get_coefficient() const; +#define INSTANTIATE_PRODUCT_PROPERTIES(HandlerTy) \ + \ + template \ + std::vector product_operator::degrees() const; \ + \ + template \ + int product_operator::n_terms() const; \ + \ + template \ + std::vector product_operator::get_terms() const; \ + \ + template \ + scalar_operator product_operator::get_coefficient() const; + +INSTANTIATE_PRODUCT_PROPERTIES(matrix_operator); // constructors @@ -180,33 +191,43 @@ product_operator::product_operator( this->coefficients = std::move(other.coefficients); } -template product_operator::product_operator( - scalar_operator coefficient); - -template product_operator::product_operator( - scalar_operator coefficient, const matrix_operator &item1); - -template product_operator::product_operator( - scalar_operator coefficient, const matrix_operator &item1, - const matrix_operator &item2); - -template product_operator::product_operator( - scalar_operator coefficient, const matrix_operator &item1, - const matrix_operator &item2, const matrix_operator &item3); - -template product_operator::product_operator( - scalar_operator coefficient, - const std::vector &atomic_operators); - -template product_operator::product_operator( - scalar_operator coefficient, - std::vector &&atomic_operators); - -template product_operator::product_operator( - const product_operator &other); - -template product_operator::product_operator( - product_operator &&other); +#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + const HandlerTy &item1); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + const HandlerTy &item1, \ + const HandlerTy &item2); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + const HandlerTy &item1, \ + const HandlerTy &item2, \ + const HandlerTy &item3); \ + \ + template \ + product_operator::product_operator( \ + scalar_operator coefficient, const std::vector &atomic_operators); \ + \ + template \ + product_operator::product_operator( \ + scalar_operator coefficient, std::vector &&atomic_operators); \ + \ + template \ + product_operator::product_operator( \ + const product_operator &other); \ + \ + template \ + product_operator::product_operator( \ + product_operator &&other); + +INSTANTIATE_PRODUCT_CONSTRUCTORS(matrix_operator); // assignments @@ -230,13 +251,17 @@ product_operator::operator=(product_operator &&other) { return *this; } -template product_operator & -product_operator::operator=( - const product_operator &other); +#define INSTANTIATE_PRODUCT_ASSIGNMENTS(HandlerTy) \ + \ + template \ + product_operator& product_operator::operator=( \ + const product_operator &other); \ + \ + template \ + product_operator& product_operator::operator=( \ + product_operator &&other); -template product_operator & -product_operator::operator=( - product_operator &&other); +INSTANTIATE_PRODUCT_ASSIGNMENTS(matrix_operator); // evaluations @@ -252,12 +277,18 @@ matrix_2 product_operator::to_matrix( return this->m_evaluate(MatrixArithmetics(dimensions, parameters)); } -template std::string product_operator::to_string() const; - -template matrix_2 product_operator::to_matrix( - std::map dimensions, +#define INSTANTIATE_PRODUCT_EVALUATIONS(HandlerTy) \ + \ + template \ + std::string product_operator::to_string() const; \ + \ + template \ + matrix_2 product_operator::to_matrix( \ + std::map dimensions, \ std::map> parameters) const; +INSTANTIATE_PRODUCT_EVALUATIONS(matrix_operator); + // comparisons template @@ -266,8 +297,13 @@ bool product_operator::operator==( throw std::runtime_error("not implemented"); } -template bool product_operator::operator==( - const product_operator &other) const; +#define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ + \ + template \ + bool product_operator::operator==( \ + const product_operator &other) const; + +INSTANTIATE_PRODUCT_COMPARISONS(matrix_operator); // unary operators @@ -282,11 +318,15 @@ product_operator product_operator::operator+() const { return *this; } -template product_operator -product_operator::operator-() const; +#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ + \ + template \ + product_operator product_operator::operator-() const; \ + \ + template \ + product_operator product_operator::operator+() const; -template product_operator -product_operator::operator+() const; +INSTANTIATE_PRODUCT_UNARY_OPS(matrix_operator); // right-hand arithmetics @@ -339,36 +379,34 @@ product_operator::operator*(const HandlerTy &other) const { PRODUCT_ADDITION_HANDLER(+) PRODUCT_ADDITION_HANDLER(-) -template product_operator -product_operator::operator*(double other) const; -template operator_sum -product_operator::operator+(double other) const; -template operator_sum -product_operator::operator-(double other) const; -template product_operator -product_operator::operator*(std::complex other) const; -template operator_sum -product_operator::operator+(std::complex other) const; -template operator_sum -product_operator::operator-(std::complex other) const; -template product_operator -product_operator::operator*( - const scalar_operator &other) const; -template operator_sum -product_operator::operator+( - const scalar_operator &other) const; -template operator_sum -product_operator::operator-( - const scalar_operator &other) const; -template product_operator -product_operator::operator*( - const matrix_operator &other) const; -template operator_sum -product_operator::operator+( - const matrix_operator &other) const; -template operator_sum -product_operator::operator-( - const matrix_operator &other) const; +#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ + \ + template \ + product_operator product_operator::operator*(double other) const; \ + template \ + operator_sum product_operator::operator+(double other) const; \ + template \ + operator_sum product_operator::operator-(double other) const; \ + template \ + product_operator product_operator::operator*(std::complex other) const; \ + template \ + operator_sum product_operator::operator+(std::complex other) const; \ + template \ + operator_sum product_operator::operator-(std::complex other) const; \ + template \ + product_operator product_operator::operator*(const scalar_operator &other) const; \ + template \ + operator_sum product_operator::operator+(const scalar_operator &other) const; \ + template \ + operator_sum product_operator::operator-(const scalar_operator &other) const; \ + template \ + product_operator product_operator::operator*(const HandlerTy &other) const; \ + template \ + operator_sum product_operator::operator+(const HandlerTy &other) const; \ + template \ + operator_sum product_operator::operator-(const HandlerTy &other) const; + +INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); template product_operator product_operator::operator*( @@ -440,24 +478,28 @@ operator_sum product_operator::operator*( PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) -template product_operator -product_operator::operator*( - const product_operator &other) const; -template operator_sum -product_operator::operator+( - const product_operator &other) const; -template operator_sum -product_operator::operator-( - const product_operator &other) const; -template operator_sum -product_operator::operator*( - const operator_sum &other) const; -template operator_sum -product_operator::operator+( - const operator_sum &other) const; -template operator_sum -product_operator::operator-( - const operator_sum &other) const; +#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + product_operator product_operator::operator*( \ + const product_operator &other) const; \ + template \ + operator_sum product_operator::operator+( \ + const product_operator &other) const; \ + template \ + operator_sum product_operator::operator-( \ + const product_operator &other) const; \ + template \ + operator_sum product_operator::operator*( \ + const operator_sum &other) const; \ + template \ + operator_sum product_operator::operator+( \ + const operator_sum &other) const; \ + template \ + operator_sum product_operator::operator-( \ + const operator_sum &other) const; + +INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); #define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -488,17 +530,20 @@ product_operator &product_operator::operator*=( return *this; } -template product_operator & -product_operator::operator*=(double other); -template product_operator & -product_operator::operator*=(std::complex other); -template product_operator & -product_operator::operator*=(const scalar_operator &other); -template product_operator & -product_operator::operator*=(const matrix_operator &other); -template product_operator & -product_operator::operator*=( - const product_operator &other); +#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ + \ + template \ + product_operator& product_operator::operator*=(double other); \ + template \ + product_operator& product_operator::operator*=(std::complex other); \ + template \ + product_operator& product_operator::operator*=(const scalar_operator &other); \ + template \ + product_operator& product_operator::operator*=(const HandlerTy &other); \ + template \ + product_operator& product_operator::operator*=(const product_operator &other); + +INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); // left-hand arithmetics @@ -551,38 +596,33 @@ product_operator operator*(const HandlerTy &other, PRODUCT_ADDITION_HANDLER_REVERSE(+) PRODUCT_ADDITION_HANDLER_REVERSE(-) -template product_operator -operator*(double other, const product_operator &self); -template product_operator -operator*(std::complex other, - const product_operator &self); -template product_operator -operator*(const scalar_operator &other, - const product_operator &self); -template product_operator -operator*(const matrix_operator &other, - const product_operator &self); -template operator_sum -operator+(double other, const product_operator &self); -template operator_sum -operator+(std::complex other, - const product_operator &self); -template operator_sum -operator+(const scalar_operator &other, - const product_operator &self); -template operator_sum -operator+(const matrix_operator &other, - const product_operator &self); -template operator_sum -operator-(double other, const product_operator &self); -template operator_sum -operator-(std::complex other, - const product_operator &self); -template operator_sum -operator-(const scalar_operator &other, - const product_operator &self); -template operator_sum -operator-(const matrix_operator &other, - const product_operator &self); +#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + product_operator operator*(double other, const product_operator &self); \ + template \ + product_operator operator*(std::complex other, const product_operator &self); \ + template \ + product_operator operator*(const scalar_operator &other, const product_operator &self); \ + template \ + product_operator operator*(const HandlerTy &other, const product_operator &self); \ + template \ + operator_sum operator+(double other, const product_operator &self); \ + template \ + operator_sum operator+(std::complex other, const product_operator &self); \ + template \ + operator_sum operator+(const scalar_operator &other, const product_operator &self); \ + template \ + operator_sum operator+(const HandlerTy &other, const product_operator &self); \ + template \ + operator_sum operator-(double other, const product_operator &self); \ + template \ + operator_sum operator-(std::complex other, const product_operator &self); \ + template \ + operator_sum operator-(const scalar_operator &other, const product_operator &self); \ + template \ + operator_sum operator-(const HandlerTy &other, const product_operator &self); + +INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index ecf1b7241d..a799042843 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -14,9 +14,7 @@ namespace cudaq { class scalar_operator; -class matrix_operator; - -template +template class product_operator; template @@ -97,73 +95,60 @@ operator_sum operator-(const HandlerTy &other, const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES -extern template product_operator -operator*(double other, const product_operator &self); -extern template operator_sum -operator+(double other, const product_operator &self); -extern template operator_sum -operator-(double other, const product_operator &self); -extern template product_operator -operator*(std::complex other, - const product_operator &self); -extern template operator_sum -operator+(std::complex other, - const product_operator &self); -extern template operator_sum -operator-(std::complex other, - const product_operator &self); -extern template product_operator -operator*(const scalar_operator &other, - const product_operator &self); -extern template operator_sum -operator+(const scalar_operator &other, - const product_operator &self); -extern template operator_sum -operator-(const scalar_operator &other, - const product_operator &self); -extern template product_operator -operator*(const matrix_operator &other, - const product_operator &self); -extern template operator_sum -operator+(const matrix_operator &other, - const product_operator &self); -extern template operator_sum -operator-(const matrix_operator &other, - const product_operator &self); +#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ + \ + extern template \ + product_operator operator*(double other, const product_operator &self); \ + extern template \ + operator_sum operator+(double other, const product_operator &self); \ + extern template \ + operator_sum operator-(double other, const product_operator &self); \ + extern template \ + product_operator operator*(std::complex other, const product_operator &self); \ + extern template \ + operator_sum operator+(std::complex other, const product_operator &self); \ + extern template \ + operator_sum operator-(std::complex other, const product_operator &self); \ + extern template \ + product_operator operator*(const scalar_operator &other, const product_operator &self); \ + extern template \ + operator_sum operator+(const scalar_operator &other, const product_operator &self); \ + extern template \ + operator_sum operator-(const scalar_operator &other, const product_operator &self); \ + extern template \ + product_operator operator*(const HandlerTy &other, const product_operator &self); \ + extern template \ + operator_sum operator+(const HandlerTy &other, const product_operator &self); \ + extern template \ + operator_sum operator-(const HandlerTy &other, const product_operator &self); \ + \ + extern template \ + operator_sum operator*(double other, const operator_sum &self); \ + extern template \ + operator_sum operator+(double other, const operator_sum &self); \ + extern template \ + operator_sum operator-(double other, const operator_sum &self); \ + extern template \ + operator_sum operator*(std::complex other, const operator_sum &self); \ + extern template \ + operator_sum operator+(std::complex other, const operator_sum &self); \ + extern template \ + operator_sum operator-(std::complex other, const operator_sum &self); \ + extern template \ + operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ + extern template \ + operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ + extern template \ + operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ + extern template \ + operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ + extern template \ + operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ + extern template \ + operator_sum operator-(const HandlerTy &other, const operator_sum &self); -extern template operator_sum -operator*(double other, const operator_sum &self); -extern template operator_sum -operator+(double other, const operator_sum &self); -extern template operator_sum -operator-(double other, const operator_sum &self); -extern template operator_sum -operator*(std::complex other, - const operator_sum &self); -extern template operator_sum -operator+(std::complex other, - const operator_sum &self); -extern template operator_sum -operator-(std::complex other, - const operator_sum &self); -extern template operator_sum -operator*(const scalar_operator &other, - const operator_sum &self); -extern template operator_sum -operator+(const scalar_operator &other, - const operator_sum &self); -extern template operator_sum -operator-(const scalar_operator &other, - const operator_sum &self); -extern template operator_sum -operator*(const matrix_operator &other, - const operator_sum &self); -extern template operator_sum -operator+(const matrix_operator &other, - const operator_sum &self); -extern template operator_sum -operator-(const matrix_operator &other, - const operator_sum &self); +class matrix_operator; +EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); #endif } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 854175e7ed..0b6d8a5ad7 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -500,112 +500,21 @@ class product_operator : public operator_sum { const product_operator &self); }; -class matrix_operator { - -private: - - static std::map m_ops; - - std::vector targets; - std::string id; +class operator_handler { public: + virtual ~operator_handler() = default; - // tools for custom operators - - /// @brief Adds the definition of an elementary operator with the given id to - /// the class. After definition, an the defined elementary operator can be - /// instantiated by providing the operator id as well as the degree(s) of - /// freedom that it acts on. An elementary operator is a parameterized object - /// acting on certain degrees of freedom. To evaluate an operator, for example - /// to compute its matrix, the level, that is the dimension, for each degree - /// of freedom it acts on must be provided, as well as all additional - /// parameters. Additional parameters must be provided in the form of keyword - /// arguments. Note: The dimensions passed during operator evaluation are - /// automatically validated against the expected dimensions specified during - /// definition - the `create` function does not need to do this. - /// @arg operator_id : A string that uniquely identifies the defined operator. - /// @arg expected_dimensions : Defines the number of levels, that is the - /// dimension, - /// for each degree of freedom in canonical (that is sorted) order. A - /// negative or zero value for one (or more) of the expected dimensions - /// indicates that the operator is defined for any dimension of the - /// corresponding degree of freedom. - /// @arg create : Takes any number of complex-valued arguments and returns the - /// matrix representing the operator in canonical order. If the matrix - /// can be defined for any number of levels for one or more degree of - /// freedom, the `create` function must take an argument called - /// `dimension` (or `dim` for short), if the operator acts on a single - /// degree of freedom, and an argument called `dimensions` (or `dims` for - /// short), if the operator acts - /// on multiple degrees of freedom. - static void define(std::string operator_id, std::vector expected_dimensions, - MatrixCallbackFunction &&create); - - // read-only properties - - /// @brief The degrees of freedom that the operator acts on in canonical - /// order. - const std::vector& degrees() const; - - // constructors and destructors - - // The constructor should never be called directly by the user: - // Keeping it internally documented for now, however. - /// @brief Constructor. - /// @arg operator_id : The ID of the operator as specified when it was - /// defined. - /// @arg degrees : the degrees of freedom that the operator acts upon. - matrix_operator(std::string operator_id, const std::vector °rees); - - // constructor - matrix_operator(std::string operator_id, std::vector &°rees); - - // copy constructor - matrix_operator(const matrix_operator &other); - - // move constructor - matrix_operator(matrix_operator &&other); - - ~matrix_operator() = default; - - // assignments - - // assignment operator - matrix_operator& operator=(const matrix_operator& other); - - // move assignment operator - matrix_operator& operator=(matrix_operator &&other); - - // evaluations + virtual const std::vector& degrees() const = 0; /// @brief Return the `matrix_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - matrix_2 to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; - - // comparisons + virtual matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const = 0; - /// @brief True, if the other value is an elementary operator with the same id - /// acting on the same degrees of freedom, and False otherwise. - bool operator==(const matrix_operator &other) const; - - // predefined operators - - static product_operator identity(int degree); - static product_operator zero(int degree); - static product_operator annihilate(int degree); - static product_operator create(int degree); - static product_operator momentum(int degree); - static product_operator number(int degree); - static product_operator parity(int degree); - static product_operator position(int degree); - /// Operators that accept parameters at runtime. - static product_operator squeeze(int degree); - static product_operator displace(int degree); }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system @@ -661,4 +570,4 @@ extern template class product_operator; extern template class operator_sum; #endif -} // namespace cudaq \ No newline at end of file +} // namespace cudaq diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp index 9b2e9af6fc..12ddddcb5e 100644 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ b/unittests/dynamics/matrix_ops_arithmetic.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "cudaq/dynamics/matrix_operators.h" #include namespace utils_0 { diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp index 8fc0642144..382cb24be9 100644 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ b/unittests/dynamics/matrix_ops_simple.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "cudaq/dynamics/matrix_operators.h" #include namespace utils { diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 783b246e0a..817359f7b3 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "cudaq/dynamics/matrix_operators.h" #include namespace utils_2 { diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operators_arithmetic.cpp index 1b038b7a40..5ecc1be87d 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operators_arithmetic.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "cudaq/operators.h" +#include "cudaq/dynamics/matrix_operators.h" #include #include From 5a85e9f6b8a46251cb9895cca85987120ac33e26 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 5 Feb 2025 20:03:43 +0000 Subject: [PATCH 255/311] trivial spin ops to set up tests before digging into perf optimizations Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/manipulation.cpp | 18 +- runtime/cudaq/dynamics/matrix_operators.cpp | 24 +- runtime/cudaq/dynamics/matrix_operators.h | 5 +- runtime/cudaq/dynamics/operator_sum.cpp | 74 ++- runtime/cudaq/dynamics/product_operators.cpp | 91 +-- runtime/cudaq/dynamics/spin_operators.cpp | 78 +++ runtime/cudaq/dynamics/spin_operators.h | 76 +++ runtime/cudaq/dynamics/templates.h | 2 + runtime/cudaq/operators.h | 13 +- unittests/CMakeLists.txt | 10 +- unittests/dynamics/matrix_operator.cpp | 628 +++++++++++++++++ unittests/dynamics/matrix_ops_arithmetic.cpp | 624 ----------------- unittests/dynamics/matrix_ops_simple.cpp | 249 ------- unittests/dynamics/operator_sum.cpp | 629 ++++++++---------- ...rs_arithmetic.cpp => product_operator.cpp} | 578 +++++++--------- ...ops_arithmetic.cpp => scalar_operator.cpp} | 118 ++++ unittests/dynamics/scalar_ops_simple.cpp | 128 ---- unittests/dynamics/spin_operator.cpp | 56 ++ unittests/dynamics/utils.cpp | 141 ++++ unittests/dynamics/utils.h | 49 ++ 20 files changed, 1768 insertions(+), 1823 deletions(-) create mode 100644 runtime/cudaq/dynamics/spin_operators.h create mode 100644 unittests/dynamics/matrix_operator.cpp delete mode 100644 unittests/dynamics/matrix_ops_arithmetic.cpp delete mode 100644 unittests/dynamics/matrix_ops_simple.cpp rename unittests/dynamics/{product_operators_arithmetic.cpp => product_operator.cpp} (61%) rename unittests/dynamics/{scalar_ops_arithmetic.cpp => scalar_operator.cpp} (80%) delete mode 100644 unittests/dynamics/scalar_ops_simple.cpp create mode 100644 unittests/dynamics/spin_operator.cpp create mode 100644 unittests/dynamics/utils.cpp create mode 100644 unittests/dynamics/utils.h diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 959294c85d..841732a381 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -96,10 +96,12 @@ MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, EvaluatedMatrix op2) { std::vector op_degrees; - op_degrees.reserve(op1.degrees().size() + op2.degrees().size()); - for (auto d : op1.degrees()) + auto op1_degrees = op1.degrees(); + auto op2_degrees = op2.degrees(); + op_degrees.reserve(op1_degrees.size() + op2_degrees.size()); + for (auto d : op1_degrees) op_degrees.push_back(d); - for (auto d : op2.degrees()) { + for (auto d : op2_degrees) { assert(std::find(op_degrees.begin(), op_degrees.end(), d) == op_degrees.end()); op_degrees.push_back(d); } @@ -114,8 +116,9 @@ EvaluatedMatrix MatrixArithmetics::mul(EvaluatedMatrix op1, // convention for how to define the matrix. Tensor products permute the // computed matrix if necessary to guarantee that all operators always have // sorted degrees. - assert(op1.degrees() == op2.degrees()); - return EvaluatedMatrix(op1.degrees(), (op1.matrix() * op2.matrix())); + auto degrees = op1.degrees(); + assert(degrees == op2.degrees()); + return EvaluatedMatrix(std::move(degrees), (op1.matrix() * op2.matrix())); } EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, @@ -124,8 +127,9 @@ EvaluatedMatrix MatrixArithmetics::add(EvaluatedMatrix op1, // convention for how to define the matrix. Tensor products permute the // computed matrix if necessary to guarantee that all operators always have // sorted degrees. - assert(op1.degrees() == op2.degrees()); - return EvaluatedMatrix(op1.degrees(), (op1.matrix() + op2.matrix())); + auto degrees = op1.degrees(); + assert(degrees == op2.degrees()); + return EvaluatedMatrix(std::move(degrees), op1.matrix() + op2.matrix()); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 6d6bcc8565..55a2cf977f 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -30,10 +30,14 @@ void matrix_operator::define(std::string operator_id, std::vector expected_ // read-only properties -const std::vector& matrix_operator::degrees() const { +std::vector matrix_operator::degrees() const { return this->targets; } +bool matrix_operator::is_identity() const { + return this->id == "identity"; +} + // constructors matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) @@ -114,24 +118,6 @@ product_operator matrix_operator::identity(int degree) { return product_operator(1., op); } -product_operator matrix_operator::zero(int degree) { - std::string op_id = "zero"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { - // Need to set the degree via the op itself because the - // argument to the outer function goes out of scope when - // the user invokes this later on via, e.g, `to_matrix()`. - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - return mat; - }; - matrix_operator::define(op_id, {-1}, func); - } - auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); -} - product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index 8489cf39b1..1bd1401644 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -58,7 +58,9 @@ class matrix_operator : operator_handler{ /// @brief The degrees of freedom that the operator acts on in canonical /// order. - virtual const std::vector& degrees() const; + virtual std::vector degrees() const; + + virtual bool is_identity() const; // constructors and destructors @@ -108,7 +110,6 @@ class matrix_operator : operator_handler{ // predefined operators static product_operator identity(int degree); - static product_operator zero(int degree); static product_operator annihilate(int degree); static product_operator create(int degree); static product_operator momentum(int degree); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 16e0294587..d5392948cd 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -11,6 +11,7 @@ #include "helpers.h" #include "manipulation.h" #include "matrix_operators.h" +#include "spin_operators.h" #include #include @@ -22,45 +23,40 @@ namespace cudaq { // private methods template -cudaq::matrix_2 -operator_sum::m_evaluate(MatrixArithmetics arithmetics, - bool pad_terms) const { +EvaluatedMatrix operator_sum::m_evaluate( + MatrixArithmetics arithmetics, bool pad_terms) const { auto terms = this->get_terms(); auto degrees = this->degrees(); // We need to make sure all matrices are of the same size to sum them up. - auto paddedTerm = [&](auto &&term) { - std::vector op_degrees; - for (auto op : term.get_terms()) { - for (auto degree : op.degrees()) - op_degrees.push_back(degree); - } - for (auto degree : degrees) { - auto it = std::find(op_degrees.begin(), op_degrees.end(), degree); - if (it == op_degrees.end()) { - term *= matrix_operator::identity(degree); + auto paddedTerm = + [&arithmetics, °rees = std::as_const(degrees)](product_operator &&term) { + auto term_degrees = term.degrees(); + for (auto degree : degrees) { + auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); + if (it == term_degrees.end()) + term *= HandlerTy::identity(degree); } - } - return term; + return term; }; if (pad_terms) { - auto padded_term = paddedTerm(terms[0]); - EvaluatedMatrix sum(degrees, padded_term.m_evaluate(arithmetics, pad_terms)); + auto padded_term = paddedTerm(std::move(terms[0])); + EvaluatedMatrix sum = padded_term.m_evaluate(arithmetics, true); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { - padded_term = paddedTerm(terms[term_idx]); - sum = arithmetics.add(std::move(sum), EvaluatedMatrix(degrees, padded_term.m_evaluate(arithmetics, pad_terms))); + padded_term = paddedTerm(std::move(terms[term_idx])); + auto term_eval = padded_term.m_evaluate(arithmetics, true); + sum = arithmetics.add(std::move(sum), std::move(term_eval)); } - return sum.matrix(); + return sum; } else { - EvaluatedMatrix sum(degrees, terms[0].m_evaluate(arithmetics, pad_terms)); + EvaluatedMatrix sum = terms[0].m_evaluate(arithmetics, false); for (auto term_idx = 1; term_idx < terms.size(); ++term_idx) { - auto term = terms[term_idx]; - auto eval = term.m_evaluate(arithmetics, pad_terms); - sum = arithmetics.add(std::move(sum), EvaluatedMatrix(degrees, eval)); + auto term_eval = terms[term_idx].m_evaluate(arithmetics, false); + sum = arithmetics.add(std::move(sum), std::move(term_eval)); } - return sum.matrix(); + return sum; } } @@ -79,7 +75,7 @@ void operator_sum::aggregate_terms( #define INSTANTIATE_SUM_PRIVATE_METHODS(HandlerTy) \ \ template \ - cudaq::matrix_2 operator_sum::m_evaluate( \ + EvaluatedMatrix operator_sum::m_evaluate( \ MatrixArithmetics arithmetics, bool pad_terms) const; \ \ /* no overload for a single product, since we don't want a constructor for a single term */ \ @@ -94,6 +90,7 @@ void operator_sum::aggregate_terms( const product_operator &item3); INSTANTIATE_SUM_PRIVATE_METHODS(matrix_operator); +INSTANTIATE_SUM_PRIVATE_METHODS(spin_operator); // read-only properties @@ -101,8 +98,10 @@ template std::vector operator_sum::degrees() const { std::set unsorted_degrees; for (const std::vector &term : this->terms) { - for (const HandlerTy &op : term) - unsorted_degrees.insert(op.degrees().begin(), op.degrees().end()); + for (const HandlerTy &op : term) { + auto op_degrees = op.degrees(); + unsorted_degrees.insert(op_degrees.begin(), op_degrees.end()); + } } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); @@ -137,6 +136,7 @@ operator_sum::get_terms() const { std::vector> operator_sum::get_terms() const; INSTANTIATE_SUM_PROPERTIES(matrix_operator); +INSTANTIATE_SUM_PROPERTIES(spin_operator); // constructors @@ -204,6 +204,7 @@ operator_sum::operator_sum(operator_sum &&other) operator_sum::operator_sum(operator_sum &&other); INSTANTIATE_SUM_CONSTRUCTORS(matrix_operator); +INSTANTIATE_SUM_CONSTRUCTORS(spin_operator); // assignments @@ -238,6 +239,7 @@ operator_sum::operator=(operator_sum &&other) { operator_sum &&other); INSTANTIATE_SUM_ASSIGNMENTS(matrix_operator); +INSTANTIATE_SUM_ASSIGNMENTS(spin_operator); // evaluations @@ -246,11 +248,10 @@ std::string operator_sum::to_string() const { throw std::runtime_error("not implemented"); } -template -matrix_2 operator_sum::to_matrix( - const std::map &dimensions, - const std::map> ¶meters) const { - return m_evaluate(MatrixArithmetics(dimensions, parameters)); +template +matrix_2 operator_sum::to_matrix(const std::map &dimensions, + const std::map> ¶meters) const { + return m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); } #define INSTANTIATE_SUM_EVALUATIONS(HandlerTy) \ @@ -264,6 +265,7 @@ matrix_2 operator_sum::to_matrix( const std::map> ¶ms) const; INSTANTIATE_SUM_EVALUATIONS(matrix_operator); +INSTANTIATE_SUM_EVALUATIONS(spin_operator); // comparisons @@ -279,6 +281,7 @@ bool operator_sum::operator==( bool operator_sum::operator==(const operator_sum &other) const; INSTANTIATE_SUM_COMPARISONS(matrix_operator); +INSTANTIATE_SUM_COMPARISONS(spin_operator); // unary operators @@ -308,6 +311,7 @@ operator_sum operator_sum::operator+() const { operator_sum operator_sum::operator+() const; INSTANTIATE_SUM_UNARY_OPS(matrix_operator); +INSTANTIATE_SUM_UNARY_OPS(spin_operator); // right-hand arithmetics @@ -428,6 +432,7 @@ SUM_ADDITION_HANDLER(-) operator_sum operator_sum::operator-(const HandlerTy &other) const; INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); +INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); template operator_sum operator_sum::operator*( @@ -552,6 +557,7 @@ SUM_ADDITION_SUM(-); const operator_sum &other) const; INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); +INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); #define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -696,6 +702,7 @@ SUM_ADDITION_SUM_ASSIGNMENT(-); operator_sum& operator_sum::operator+=(const operator_sum &other); INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); +INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); // left-hand arithmetics @@ -816,5 +823,6 @@ SUM_ADDITION_HANDLER_REVERSE(-) operator_sum operator-(const HandlerTy &other, const operator_sum &self); INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); +INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index c75f542f7b..d575638ebb 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -11,6 +11,7 @@ #include "helpers.h" #include "manipulation.h" #include "matrix_operators.h" +#include "spin_operators.h" #include #include @@ -34,58 +35,53 @@ void product_operator::aggregate_terms(const HandlerTy &head, Args&& // FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - // IT SUPPOSED TO TAKE A TRANSFORMATION (ANY OPERATOR ARITHMETICS) AND APPLY IT template -cudaq::matrix_2 -product_operator::m_evaluate(MatrixArithmetics arithmetics, - bool pad_terms) const { +EvaluatedMatrix product_operator::m_evaluate( + MatrixArithmetics arithmetics, bool pad_terms) const { const std::vector &terms = this->terms[0]; auto degrees = this->degrees(); cudaq::matrix_2 result; - auto padded_op = [](MatrixArithmetics &arithmetics, const HandlerTy &op, - std::vector degrees, std::map dimensions, - std::map> parameters) { - std::vector padded; - for (const auto °ree : degrees) { - if (std::find(op.degrees().begin(), op.degrees().end(), degree) == op.degrees().end()) { - padded.push_back(EvaluatedMatrix({degree}, matrix_operator::identity(degree).to_matrix(dimensions))); + auto padded_op = [&arithmetics, °rees = std::as_const(degrees)](const HandlerTy &op) { + std::vector padded; + auto op_degrees = op.degrees(); + for (const auto °ree : degrees) { + if (std::find(op_degrees.begin(), op_degrees.end(), degree) == op_degrees.end()) { + // FIXME: instead of relying on an identity to exist, replace pad_terms with a function to invoke. + auto identity = HandlerTy::identity(degree); + padded.push_back(EvaluatedMatrix(identity.degrees(), identity.to_matrix(arithmetics.m_dimensions))); + } } - } - /// Creating the tensor product with op being last is most efficient. - if (padded.size() == 0) - return EvaluatedMatrix(op.degrees(), op.to_matrix(dimensions, parameters)); - EvaluatedMatrix ids(std::move(padded[0])); - for (auto i = 1; i < padded.size(); ++i) - ids = arithmetics.tensor(std::move(ids), std::move(padded[i])); - return arithmetics.tensor(std::move(ids), EvaluatedMatrix(op.degrees(), op.to_matrix(dimensions, parameters))); + /// Creating the tensor product with op being last is most efficient. + if (padded.size() == 0) + return EvaluatedMatrix(op_degrees, op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); + EvaluatedMatrix ids(std::move(padded[0])); + for (auto i = 1; i < padded.size(); ++i) + ids = arithmetics.tensor(std::move(ids), std::move(padded[i])); + return arithmetics.tensor(std::move(ids), EvaluatedMatrix(op_degrees, op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters))); }; + auto coefficient = this->coefficients[0].evaluate(arithmetics.m_parameters); if (terms.size() > 0) { if (pad_terms) { - EvaluatedMatrix evaluated(padded_op(arithmetics, terms[0], degrees, arithmetics.m_dimensions, arithmetics.m_parameters)); + EvaluatedMatrix prod = padded_op(terms[0]); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - const HandlerTy &op = terms[op_idx]; - if (op.degrees().size() != 1 || op != cudaq::matrix_operator("identity", op.degrees())) { - auto padded = padded_op(arithmetics, op, degrees, arithmetics.m_dimensions, arithmetics.m_parameters); - evaluated = arithmetics.mul(std::move(evaluated), std::move(padded)); - } + auto op_degrees = terms[op_idx].degrees(); + if (op_degrees.size() != 1 || !terms[op_idx].is_identity()) + prod = arithmetics.mul(std::move(prod), padded_op(terms[op_idx])); } - result = evaluated.matrix(); + return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); } else { - EvaluatedMatrix evaluated(terms[0].degrees(), terms[0].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); + EvaluatedMatrix prod(terms[0].degrees(), terms[0].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto &op = terms[op_idx]; - auto mat = op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); - evaluated = arithmetics.mul(std::move(evaluated), EvaluatedMatrix(op.degrees(), mat)); + auto mat = terms[op_idx].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + prod = arithmetics.mul(std::move(prod), EvaluatedMatrix(terms[op_idx].degrees(), mat)); } - result = evaluated.matrix(); + return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); } } else { - auto full_hilbert_size = 1; - for (const auto degree : degrees) - full_hilbert_size *= arithmetics.m_dimensions[degree]; - result = cudaq::matrix_2::identity(full_hilbert_size); + assert(degrees.size() == 0); // degrees are stored with each term + return EvaluatedMatrix({}, coefficient * cudaq::matrix_2::identity(1)); } - return this->coefficients[0].evaluate(arithmetics.m_parameters) * result; } #define INSTANTIATE_PRODUCT_PRIVATE_METHODS(HandlerTy) \ @@ -100,10 +96,11 @@ product_operator::m_evaluate(MatrixArithmetics arithmetics, const HandlerTy &item3); \ \ template \ - cudaq::matrix_2 product_operator::m_evaluate( \ + EvaluatedMatrix product_operator::m_evaluate( \ MatrixArithmetics arithmetics, bool pad_terms) const; INSTANTIATE_PRODUCT_PRIVATE_METHODS(matrix_operator); +INSTANTIATE_PRODUCT_PRIVATE_METHODS(spin_operator); // read-only properties @@ -111,7 +108,8 @@ template std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->terms[0]) { - unsorted_degrees.insert(term.degrees().begin(), term.degrees().end()); + auto term_degrees = term.degrees(); + unsorted_degrees.insert(term_degrees.begin(), term_degrees.end()); } auto degrees = std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); @@ -148,6 +146,7 @@ scalar_operator product_operator::get_coefficient() const { scalar_operator product_operator::get_coefficient() const; INSTANTIATE_PRODUCT_PROPERTIES(matrix_operator); +INSTANTIATE_PRODUCT_PROPERTIES(spin_operator); // constructors @@ -228,6 +227,7 @@ product_operator::product_operator( product_operator &&other); INSTANTIATE_PRODUCT_CONSTRUCTORS(matrix_operator); +INSTANTIATE_PRODUCT_CONSTRUCTORS(spin_operator); // assignments @@ -262,6 +262,7 @@ product_operator::operator=(product_operator &&other) { product_operator &&other); INSTANTIATE_PRODUCT_ASSIGNMENTS(matrix_operator); +INSTANTIATE_PRODUCT_ASSIGNMENTS(spin_operator); // evaluations @@ -270,11 +271,10 @@ std::string product_operator::to_string() const { throw std::runtime_error("not implemented"); } -template -matrix_2 product_operator::to_matrix( - std::map dimensions, - std::map> parameters) const { - return this->m_evaluate(MatrixArithmetics(dimensions, parameters)); +template +matrix_2 product_operator::to_matrix(std::map dimensions, + std::map> parameters) const { + return this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); } #define INSTANTIATE_PRODUCT_EVALUATIONS(HandlerTy) \ @@ -288,6 +288,7 @@ matrix_2 product_operator::to_matrix( std::map> parameters) const; INSTANTIATE_PRODUCT_EVALUATIONS(matrix_operator); +INSTANTIATE_PRODUCT_EVALUATIONS(spin_operator); // comparisons @@ -304,6 +305,7 @@ bool product_operator::operator==( const product_operator &other) const; INSTANTIATE_PRODUCT_COMPARISONS(matrix_operator); +INSTANTIATE_PRODUCT_COMPARISONS(spin_operator); // unary operators @@ -327,6 +329,7 @@ product_operator product_operator::operator+() const { product_operator product_operator::operator+() const; INSTANTIATE_PRODUCT_UNARY_OPS(matrix_operator); +INSTANTIATE_PRODUCT_UNARY_OPS(spin_operator); // right-hand arithmetics @@ -407,6 +410,7 @@ PRODUCT_ADDITION_HANDLER(-) operator_sum product_operator::operator-(const HandlerTy &other) const; INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); +INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); template product_operator product_operator::operator*( @@ -500,6 +504,7 @@ PRODUCT_ADDITION_SUM(-) const operator_sum &other) const; INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); +INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(spin_operator); #define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -544,6 +549,7 @@ product_operator &product_operator::operator*=( product_operator& product_operator::operator*=(const product_operator &other); INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); +INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); // left-hand arithmetics @@ -624,5 +630,6 @@ PRODUCT_ADDITION_HANDLER_REVERSE(-) operator_sum operator-(const HandlerTy &other, const product_operator &self); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); +INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index a1cd462520..e40d782e5e 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -6,3 +6,81 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/operators.h" +#include "spin_operators.h" +#include + +namespace cudaq { + +// read-only properties + +std::vector spin_operator::degrees() const { + return {this->target}; +} + +// constructors + +spin_operator::spin_operator(int op_id, int target) + : id(op_id), target(target) { + assert(0 <= op_id < 4); +} + +bool spin_operator::is_identity() const { + return this->id == 0; +} + +// evaluations + +matrix_2 spin_operator::to_matrix(std::map dimensions, + std::map> parameters) const { + auto it = dimensions.find(this->target); + if (it != dimensions.end() && it->second != 2) + throw std::runtime_error("dimension for spin operator must be 2"); + + // FIXME: CHECK CONVENTIONS + auto mat = matrix_2(2, 2); + if (this->id == 1) { // Z + mat[{0, 0}] = 1.0; + mat[{1, 1}] = -1.0; + } else if (this->id == 2) { // X + mat[{0, 1}] = 1.0; + mat[{1, 0}] = 1.0; + } else if (this->id == 3) { // Y + mat[{0, 1}] = -1.0j; + mat[{1, 0}] = 1.0j; + } else { // I + mat[{0, 0}] = 1.0; + mat[{1, 1}] = 1.0; + } + return mat; +} + +// comparisons + +bool spin_operator::operator==(const spin_operator &other) const { + return this->id == other.id && this->target == other.target; +} + +// defined operators + +spin_operator spin_operator::identity(int degree) { + return spin_operator(0, degree); +} + +product_operator spin_operator::i(int degree) { + return product_operator(1., spin_operator(0, degree)); +} + +product_operator spin_operator::z(int degree) { + return product_operator(1., spin_operator(1, degree)); +} + +product_operator spin_operator::x(int degree) { + return product_operator(1., spin_operator(2, degree)); +} + +product_operator spin_operator::y(int degree) { + return product_operator(1., spin_operator(3, degree)); +} + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h new file mode 100644 index 0000000000..8e0128895f --- /dev/null +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" + +#include + +namespace cudaq { + +// FIXME: rename to spin ... +class spin_operator : operator_handler{ + +private: + + // I = 0, Z = 1, X = 2, Y = 3 + int id; + int target; + + spin_operator(int id, int target); + +public: + + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + virtual std::vector degrees() const; + + virtual bool is_identity() const; + + // constructors and destructors + + ~spin_operator() = default; + + // assignments + + // evaluations + + /// @brief Return the `matrix_operator` as a matrix. + /// @arg `dimensions` : A map specifying the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0 : 2, 1 : 2}`. + virtual matrix_2 to_matrix(std::map dimensions = {}, + std::map> parameters = {}) const; + + // comparisons + + bool operator==(const spin_operator &other) const; + + // defined operators + + static spin_operator identity(int degree); + static product_operator i(int degree); + static product_operator z(int degree); + static product_operator x(int degree); + static product_operator y(int degree); +}; + +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +template class product_operator; +template class operator_sum; +#else +extern template class product_operator; +extern template class operator_sum; +#endif + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index a799042843..5e3ccdd85e 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -148,7 +148,9 @@ operator_sum operator-(const HandlerTy &other, operator_sum operator-(const HandlerTy &other, const operator_sum &self); class matrix_operator; +class spin_operator; EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); +EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); #endif } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 0b6d8a5ad7..697de8dfb3 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -22,6 +22,7 @@ namespace cudaq { class MatrixArithmetics; +class EvaluatedMatrix; class scalar_operator { @@ -151,7 +152,7 @@ class operator_sum { private: - matrix_2 m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; void aggregate_terms(); @@ -340,11 +341,10 @@ class product_operator : public operator_sum { template void aggregate_terms(const HandlerTy &head, Args &&...args); - matrix_2 m_evaluate(MatrixArithmetics arithmetics, - bool pad_terms = true) const; + EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; template - friend matrix_2 operator_sum::m_evaluate(MatrixArithmetics arithmetics, bool pad_terms) const; + friend EvaluatedMatrix operator_sum::m_evaluate(MatrixArithmetics arithmetics, bool pad_terms) const; public: // read-only properties @@ -505,7 +505,9 @@ class operator_handler { public: virtual ~operator_handler() = default; - virtual const std::vector& degrees() const = 0; + virtual std::vector degrees() const = 0; + + virtual bool is_identity() const = 0; /// @brief Return the `matrix_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, @@ -514,7 +516,6 @@ class operator_handler { /// degrees of freedom: `{0 : 2, 1 : 2}`. virtual matrix_2 to_matrix(std::map dimensions = {}, std::map> parameters = {}) const = 0; - }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 98e7841575..d3b99bcf3c 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -264,12 +264,12 @@ gtest_discover_tests(test_spin) # Create an executable for operators UnitTests set(CUDAQ_OPERATOR_TEST_SOURCES + dynamics/utils.cpp dynamics/operator_sum.cpp - dynamics/matrix_ops_simple.cpp - dynamics/matrix_ops_arithmetic.cpp - dynamics/scalar_ops_simple.cpp - dynamics/scalar_ops_arithmetic.cpp - dynamics/product_operators_arithmetic.cpp + dynamics/spin_operator.cpp + dynamics/matrix_operator.cpp + dynamics/scalar_operator.cpp + dynamics/product_operator.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp new file mode 100644 index 0000000000..89c73d5aaf --- /dev/null +++ b/unittests/dynamics/matrix_operator.cpp @@ -0,0 +1,628 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "utils.h" +#include "cudaq/operators.h" +#include + +TEST(OperatorExpressions, checkElementaryUnary) { + auto create = cudaq::matrix_operator::create(0); + utils::checkEqual((-create).to_matrix({{0,2}}), -1.0 * utils::create_matrix(2)); +} + +TEST(OperatorExpressions, checkPreBuiltMatrixOps) { + std::vector levels = {2, 3, 4, 5}; + + // Keeping this fixed throughout. + int degree_index = 0; + + // Identity operator. + { + for (auto level_count : levels) { + auto id = cudaq::matrix_operator::identity(degree_index); + auto got_id = id.to_matrix({{degree_index, level_count}}); + auto want_id = utils::id_matrix(level_count); + utils::checkEqual(want_id, got_id); + } + } + + // Annihilation operator. + { + for (auto level_count : levels) { + auto annihilate = cudaq::matrix_operator::annihilate(degree_index); + auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); + auto want_annihilate = utils::annihilate_matrix(level_count); + utils::checkEqual(want_annihilate, got_annihilate); + } + } + + // Creation operator. + { + for (auto level_count : levels) { + auto create = cudaq::matrix_operator::create(degree_index); + auto got_create = create.to_matrix({{degree_index, level_count}}); + auto want_create = utils::create_matrix(level_count); + utils::checkEqual(want_create, got_create); + } + } + + // Position operator. + { + for (auto level_count : levels) { + auto position = cudaq::matrix_operator::position(degree_index); + auto got_position = position.to_matrix({{degree_index, level_count}}); + auto want_position = utils::position_matrix(level_count); + utils::checkEqual(want_position, got_position); + } + } + + // Momentum operator. + { + for (auto level_count : levels) { + auto momentum = cudaq::matrix_operator::momentum(degree_index); + auto got_momentum = momentum.to_matrix({{degree_index, level_count}}); + auto want_momentum = utils::momentum_matrix(level_count); + utils::checkEqual(want_momentum, got_momentum); + } + } + + // Number operator. + { + for (auto level_count : levels) { + auto number = cudaq::matrix_operator::number(degree_index); + auto got_number = number.to_matrix({{degree_index, level_count}}); + auto want_number = utils::number_matrix(level_count); + utils::checkEqual(want_number, got_number); + } + } + + // Parity operator. + { + for (auto level_count : levels) { + auto parity = cudaq::matrix_operator::parity(degree_index); + auto got_parity = parity.to_matrix({{degree_index, level_count}}); + auto want_parity = utils::parity_matrix(level_count); + utils::checkEqual(want_parity, got_parity); + } + } + + // Displacement operator. + { + for (auto level_count : levels) { + auto displacement = 2.0 + 1.0j; + auto displace = cudaq::matrix_operator::displace(degree_index); + auto got_displace = displace.to_matrix({{degree_index, level_count}}, + {{"displacement", displacement}}); + auto want_displace = utils::displace_matrix(level_count, displacement); + utils::checkEqual(want_displace, got_displace); + } + } + + // Squeeze operator. + { + for (auto level_count : levels) { + auto squeezing = 2.0 + 1.0j; + auto squeeze = cudaq::matrix_operator::squeeze(degree_index); + auto got_squeeze = squeeze.to_matrix({{degree_index, level_count}}, + {{"squeezing", squeezing}}); + auto want_squeeze = utils::squeeze_matrix(level_count, squeezing); + utils::checkEqual(want_squeeze, got_squeeze); + } + } +} + +TEST(OperatorExpressions, checkCustomMatrixOps) { + auto level_count = 2; + std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}}; + + { + auto func0 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); + auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); + + auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), + utils::position_matrix(level_count + 2)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), + utils::number_matrix(level_count)); + + utils::checkEqual(op0.to_matrix(dimensions), matrix0); + utils::checkEqual(op1.to_matrix(dimensions), matrix1); +} + +TEST(OperatorExpressions, checkElementaryAgainstDouble) { + std::complex value = 0.125 + 0.125j; + + // `matrix_operator` + `complex` and `complex` + + // `matrix_operator` + { + auto elementary = cudaq::matrix_operator::annihilate(0); + + auto sum = value + elementary; + auto reverse = elementary + value; + + auto got_matrix = sum.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils::id_matrix(3); + auto want_matrix = scaled_identity + utils::annihilate_matrix(3); + auto want_matrix_reverse = utils::annihilate_matrix(3) + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `matrix_operator` - `complex` and `complex` - `matrix_operator` + { + auto elementary = cudaq::matrix_operator::position(0); + + auto difference = value - elementary; + auto reverse = elementary - value; + + auto got_matrix = difference.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils::id_matrix(3); + auto want_matrix = scaled_identity - utils::position_matrix(3); + auto want_matrix_reverse = utils::position_matrix(3) - scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `matrix_operator` * `complex` and `complex` * + // `matrix_operator` + { + auto elementary = cudaq::matrix_operator::number(0); + + auto product = value * elementary; + auto reverse = elementary * value; + + auto got_matrix = product.to_matrix({{0,3}}); + auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); + + auto scaled_identity = value * utils::id_matrix(3); + auto want_matrix = scaled_identity * utils::number_matrix(3); + auto want_matrix_reverse = utils::number_matrix(3) * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + +TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { + + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + /// Keeping these fixed for these more simple tests. + int level_count = 3; + int degree_index = 0; + double const_scale_factor = 2.0; + + // `matrix_operator + scalar_operator` + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = + utils::annihilate_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils::annihilate_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `matrix_operator + scalar_operator` + { + auto self = cudaq::matrix_operator::parity(0); + auto other = cudaq::scalar_operator(function); + + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::parity_matrix(level_count) + scaled_identity; + auto want_reverse_matrix = + scaled_identity + utils::parity_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `matrix_operator - scalar_operator` + { + auto self = cudaq::matrix_operator::number(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = utils::number_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils::number_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `matrix_operator - scalar_operator` + { + auto self = cudaq::matrix_operator::position(0); + auto other = cudaq::scalar_operator(function); + + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::position_matrix(level_count) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils::position_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `matrix_operator * scalar_operator` + { + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto product = self * other; + auto reverse = other * self; + + utils::assert_product_equal(product, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + utils::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); + auto got_matrix = product.to_matrix({{degree_index, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); + auto want_matrix = utils::momentum_matrix(level_count) * scaled_identity; + auto want_reverse_matrix = + scaled_identity * utils::momentum_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `matrix_operator * scalar_operator` + { + auto self = cudaq::matrix_operator::create(0); + auto other = cudaq::scalar_operator(function); + + auto product = self * other; + auto reverse = other * self; + + utils::assert_product_equal(product, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + utils::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::create_matrix(level_count) * scaled_identity; + auto want_reverse_matrix = + scaled_identity * utils::create_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } +} + +/// Prebuilt elementary ops against one another. +TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { + + /// Keeping this fixed throughout. + int level_count = 3; + std::map dimensions = {{0, level_count}, {1, level_count}}; + + // Addition, same DOF. + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); + + auto sum = self + other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = utils::annihilate_matrix(level_count) + + utils::create_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + } + + // Addition, different DOF's. + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); + + auto sum = self + other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils::create_matrix(level_count), + utils::id_matrix(level_count)); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto want_matrix = annihilate_full + create_full; + utils::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, same DOF. + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); + + auto sum = self - other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = utils::annihilate_matrix(level_count) - + utils::create_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, different DOF's. + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); + + auto sum = self - other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils::create_matrix(level_count), + utils::id_matrix(level_count)); + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = annihilate_full - create_full; + utils::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, same DOF. + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(0); + + auto product = self * other; + ASSERT_TRUE(product.n_terms() == 2); + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto got_matrix = product.to_matrix(dimensions); + auto want_matrix = utils::annihilate_matrix(level_count) * + utils::create_matrix(level_count); + utils::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, different DOF's. + { + auto self = cudaq::matrix_operator::annihilate(0); + auto other = cudaq::matrix_operator::create(1); + + auto product = self * other; + ASSERT_TRUE(product.n_terms() == 2); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto create_full = cudaq::kronecker(utils::create_matrix(level_count), + utils::id_matrix(level_count)); + auto got_matrix = product.to_matrix(dimensions); + auto want_matrix = annihilate_full * create_full; + utils::checkEqual(want_matrix, got_matrix); + } +} + +/// Testing arithmetic between elementary operators and operator +/// sums. +TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { + + /// Keeping this fixed throughout. + int level_count = 3; + std::complex value = 0.125 + 0.5j; + + /// `matrix_operator + operator_sum` and `operator_sum + + /// matrix_operator` + { + auto self = cudaq::matrix_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + + auto got = self + operator_sum; + auto reverse = operator_sum + self; + + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto self_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::id_matrix(level_count)); + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto want_matrix = self_full + term_0_full + term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full + self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `matrix_operator - operator_sum` and `operator_sum - + /// matrix_operator` + { + auto self = cudaq::matrix_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + + auto got = self - operator_sum; + auto reverse = operator_sum - self; + + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto self_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::id_matrix(level_count)); + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto want_matrix = self_full - term_0_full - term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full - self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `matrix_operator * operator_sum` and `operator_sum * + /// matrix_operator` + { + auto self = cudaq::matrix_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::squeeze(0) + + cudaq::matrix_operator::identity(1); + + auto got = self * operator_sum; + auto reverse = operator_sum * self; + + ASSERT_TRUE(got.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + for (auto &term : got.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + for (auto &term : reverse.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + + auto self_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::squeeze_matrix(level_count, value)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::id_matrix(level_count)); + auto sum_full = term_0_full + term_1_full; + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto want_matrix = self_full * sum_full; + auto want_reverse_matrix = sum_full * self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `operator_sum += matrix_operator` + { + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + operator_sum += cudaq::matrix_operator::displace(0); + + ASSERT_TRUE(operator_sum.n_terms() == 3); + + auto self_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::displace_matrix(level_count, value)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::id_matrix(level_count)); + + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}, {{"displacement", value}}); + auto want_matrix = term_0_full + term_1_full + self_full; + utils::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum -= matrix_operator` + { + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + operator_sum -= cudaq::matrix_operator::annihilate(0); + + ASSERT_TRUE(operator_sum.n_terms() == 3); + + auto self_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::id_matrix(level_count)); + + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto want_matrix = term_0_full + term_1_full - self_full; + utils::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum *= matrix_operator` + { + auto self = cudaq::matrix_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::create(0) + + cudaq::matrix_operator::identity(1); + + operator_sum *= self; + + ASSERT_TRUE(operator_sum.n_terms() == 2); + for (auto &term : operator_sum.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + + auto self_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::create_matrix(level_count)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), + utils::id_matrix(level_count)); + auto sum_full = term_0_full + term_1_full; + + auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto want_matrix = sum_full * self_full; + utils::checkEqual(want_matrix, got_matrix); + } +} diff --git a/unittests/dynamics/matrix_ops_arithmetic.cpp b/unittests/dynamics/matrix_ops_arithmetic.cpp deleted file mode 100644 index 12ddddcb5e..0000000000 --- a/unittests/dynamics/matrix_ops_arithmetic.cpp +++ /dev/null @@ -1,624 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/operators.h" -#include "cudaq/dynamics/matrix_operators.h" -#include - -namespace utils_0 { -void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { - ASSERT_EQ(a.get_rank(), b.get_rank()); - ASSERT_EQ(a.get_rows(), b.get_rows()); - ASSERT_EQ(a.get_columns(), b.get_columns()); - ASSERT_EQ(a.get_size(), b.get_size()); - for (std::size_t i = 0; i < a.get_rows(); i++) { - for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_real = a[{i, j}].real(); - double b_real = b[{i, j}].real(); - EXPECT_NEAR(a_real, b_real, 1e-8); - double a_imag = a[{i, j}].imag(); - double b_imag = b[{i, j}].imag(); - EXPECT_NEAR(a_imag, b_imag, 1e-8); - } - } -} - -cudaq::matrix_2 zero_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - return mat; -} - -cudaq::matrix_2 id_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = 1.0 + 0.0j; - return mat; -} - -cudaq::matrix_2 annihilate_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 create_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 position_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 momentum_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = - (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = - -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 number_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = static_cast(i) + 0.0j; - return mat; -} - -cudaq::matrix_2 parity_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; - return mat; -} - -cudaq::matrix_2 displace_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = amplitude * create_matrix(size); - auto term2 = std::conj(amplitude) * annihilate_matrix(size); - auto difference = term1 - term2; - return difference.exponential(); -} - -cudaq::matrix_2 squeeze_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); - auto term2 = amplitude * create_matrix(size).power(2); - auto difference = 0.5 * (term1 - term2); - return difference.exponential(); -} - -void assert_product_equal( - const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { - - auto sumterms_prod = - ((const cudaq::operator_sum &)got).get_terms(); - ASSERT_TRUE(sumterms_prod.size() == 1); - ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); - ASSERT_TRUE(got.get_terms() == expected_terms); -} - -} // namespace utils_0 - -TEST(OperatorExpressions, checkElementaryUnary) { - auto id = cudaq::matrix_operator::identity(0); - utils_0::checkEqual((-id).to_matrix({{0,2}}), -1.0 * utils_0::id_matrix(2)); -} - -TEST(OperatorExpressions, checkElementaryAgainstDouble) { - std::complex value = 0.125 + 0.125j; - - // `matrix_operator` + `complex` and `complex` + - // `matrix_operator` - { - auto elementary = cudaq::matrix_operator::annihilate(0); - - auto sum = value + elementary; - auto reverse = elementary + value; - - auto got_matrix = sum.to_matrix({{0, 3}}); - auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); - - auto scaled_identity = value * utils_0::id_matrix(3); - auto want_matrix = scaled_identity + utils_0::annihilate_matrix(3); - auto want_matrix_reverse = utils_0::annihilate_matrix(3) + scaled_identity; - - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `matrix_operator` - `complex` and `complex` - - // `matrix_operator` - { - auto elementary = cudaq::matrix_operator::position(0); - - auto difference = value - elementary; - auto reverse = elementary - value; - - auto got_matrix = difference.to_matrix({{0, 3}}); - auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); - - auto scaled_identity = value * utils_0::id_matrix(3); - auto want_matrix = scaled_identity - utils_0::position_matrix(3); - auto want_matrix_reverse = utils_0::position_matrix(3) - scaled_identity; - - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `matrix_operator` * `complex` and `complex` * - // `matrix_operator` - { - auto elementary = cudaq::matrix_operator::number(0); - - auto product = value * elementary; - auto reverse = elementary * value; - - auto got_matrix = product.to_matrix({{0, 3}}); - auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); - - auto scaled_identity = value * utils_0::id_matrix(3); - auto want_matrix = scaled_identity * utils_0::number_matrix(3); - auto want_matrix_reverse = utils_0::number_matrix(3) * scaled_identity; - - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_matrix_reverse, got_matrix_reverse); - } -} - -TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { - - auto function = [](std::map> parameters) { - return parameters["value"]; - }; - - /// Keeping these fixed for these more simple tests. - int level_count = 3; - int degree_index = 0; - double const_scale_factor = 2.0; - - // `matrix_operator + scalar_operator` - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::scalar_operator(const_scale_factor); - - auto sum = self + other; - auto reverse = other + self; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); - auto want_matrix = - utils_0::annihilate_matrix(level_count) + scaled_identity; - auto want_reverse_matrix = - scaled_identity + utils_0::annihilate_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - // `matrix_operator + scalar_operator` - { - auto self = cudaq::matrix_operator::parity(0); - auto other = cudaq::scalar_operator(function); - - auto sum = self + other; - auto reverse = other + self; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, - {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix( - {{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto want_matrix = utils_0::parity_matrix(level_count) + scaled_identity; - auto want_reverse_matrix = - scaled_identity + utils_0::parity_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - // `matrix_operator - scalar_operator` - { - auto self = cudaq::matrix_operator::number(0); - auto other = cudaq::scalar_operator(const_scale_factor); - - auto sum = self - other; - auto reverse = other - self; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); - auto want_matrix = utils_0::number_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = - scaled_identity - utils_0::number_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - // `matrix_operator - scalar_operator` - { - auto self = cudaq::matrix_operator::position(0); - auto other = cudaq::scalar_operator(function); - - auto sum = self - other; - auto reverse = other - self; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, - {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix( - {{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto want_matrix = utils_0::position_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = - scaled_identity - utils_0::position_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - // `matrix_operator * scalar_operator` - { - auto self = cudaq::matrix_operator::momentum(0); - auto other = cudaq::scalar_operator(const_scale_factor); - - auto product = self * other; - auto reverse = other * self; - - utils_0::assert_product_equal(product, const_scale_factor, - {cudaq::matrix_operator("momentum", {0})}); - utils_0::assert_product_equal(reverse, const_scale_factor, - {cudaq::matrix_operator("momentum", {0})}); - - std::vector want_degrees = {0}; - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); - auto want_matrix = utils_0::momentum_matrix(level_count) * scaled_identity; - auto want_reverse_matrix = - scaled_identity * utils_0::momentum_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - // `matrix_operator * scalar_operator` - { - auto self = cudaq::matrix_operator::create(0); - auto other = cudaq::scalar_operator(function); - - auto product = self * other; - auto reverse = other * self; - - utils_0::assert_product_equal(product, other.evaluate(), - {cudaq::matrix_operator("create", {0})}); - utils_0::assert_product_equal(reverse, other.evaluate(), - {cudaq::matrix_operator("create", {0})}); - - std::vector want_degrees = {0}; - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - - auto scaled_identity = const_scale_factor * utils_0::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}, - {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix( - {{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto want_matrix = utils_0::create_matrix(level_count) * scaled_identity; - auto want_reverse_matrix = - scaled_identity * utils_0::create_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } -} - -/// Prebuilt elementary ops against one another. -TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { - - /// Keeping this fixed throughout. - int level_count = 3; - std::map dimensions = {{0, level_count}, {1, level_count}}; - - // Addition, same DOF. - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(0); - - auto sum = self + other; - ASSERT_TRUE(sum.n_terms() == 2); - - auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = utils_0::annihilate_matrix(level_count) + - utils_0::create_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - } - - // Addition, different DOF's. - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(1); - - auto sum = self + other; - ASSERT_TRUE(sum.n_terms() == 2); - - auto annihilate_full = - cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), - utils_0::id_matrix(level_count)); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); - auto want_matrix = annihilate_full + create_full; - utils_0::checkEqual(want_matrix, got_matrix); - } - - // Subtraction, same DOF. - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(0); - - auto sum = self - other; - ASSERT_TRUE(sum.n_terms() == 2); - - auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = utils_0::annihilate_matrix(level_count) - - utils_0::create_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - } - - // Subtraction, different DOF's. - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(1); - - auto sum = self - other; - ASSERT_TRUE(sum.n_terms() == 2); - - auto annihilate_full = - cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), - utils_0::id_matrix(level_count)); - auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = annihilate_full - create_full; - utils_0::checkEqual(want_matrix, got_matrix); - } - - // Multiplication, same DOF. - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(0); - - auto product = self * other; - ASSERT_TRUE(product.n_terms() == 2); - - std::vector want_degrees = {0}; - ASSERT_TRUE(product.degrees() == want_degrees); - - auto got_matrix = product.to_matrix(dimensions); - auto want_matrix = utils_0::annihilate_matrix(level_count) * - utils_0::create_matrix(level_count); - utils_0::checkEqual(want_matrix, got_matrix); - } - - // Multiplication, different DOF's. - { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(1); - - auto product = self * other; - ASSERT_TRUE(product.n_terms() == 2); - - std::vector want_degrees = {1, 0}; - ASSERT_TRUE(product.degrees() == want_degrees); - - auto annihilate_full = - cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto create_full = cudaq::kronecker(utils_0::create_matrix(level_count), - utils_0::id_matrix(level_count)); - auto got_matrix = product.to_matrix(dimensions); - auto want_matrix = annihilate_full * create_full; - utils_0::checkEqual(want_matrix, got_matrix); - } -} - -/// Testing arithmetic between elementary operators and operator -/// sums. -TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { - - /// Keeping this fixed throughout. - int level_count = 3; - std::complex value = 0.125 + 0.5j; - - /// `matrix_operator + operator_sum` and `operator_sum + - /// matrix_operator` - { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); - - auto got = self + operator_sum; - auto reverse = operator_sum + self; - - ASSERT_TRUE(got.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); - - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); - auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::id_matrix(level_count)); - - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto want_matrix = self_full + term_0_full + term_1_full; - auto want_reverse_matrix = term_0_full + term_1_full + self_full; - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - /// `matrix_operator - operator_sum` and `operator_sum - - /// matrix_operator` - { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); - - auto got = self - operator_sum; - auto reverse = operator_sum - self; - - ASSERT_TRUE(got.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); - - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); - auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::id_matrix(level_count)); - - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto want_matrix = self_full - term_0_full - term_1_full; - auto want_reverse_matrix = term_0_full + term_1_full - self_full; - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - /// `matrix_operator * operator_sum` and `operator_sum * - /// matrix_operator` - { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::squeeze(0) + - cudaq::matrix_operator::identity(1); - - auto got = self * operator_sum; - auto reverse = operator_sum * self; - - ASSERT_TRUE(got.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - for (auto &term : got.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); - for (auto &term : reverse.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); - - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto term_0_full = - cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::squeeze_matrix(level_count, value)); - auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::id_matrix(level_count)); - auto sum_full = term_0_full + term_1_full; - - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, - {{"squeezing", value}}); - auto got_reverse_matrix = reverse.to_matrix( - {{0, level_count}, {1, level_count}}, {{"squeezing", value}}); - auto want_matrix = self_full * sum_full; - auto want_reverse_matrix = sum_full * self_full; - utils_0::checkEqual(want_matrix, got_matrix); - utils_0::checkEqual(want_reverse_matrix, got_reverse_matrix); - } - - /// `operator_sum += matrix_operator` - { - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); - operator_sum += cudaq::matrix_operator::displace(0); - - ASSERT_TRUE(operator_sum.n_terms() == 3); - - auto self_full = - cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::displace_matrix(level_count, value)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); - auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::id_matrix(level_count)); - - auto got_matrix = operator_sum.to_matrix( - {{0, level_count}, {1, level_count}}, {{"displacement", value}}); - auto want_matrix = term_0_full + term_1_full + self_full; - utils_0::checkEqual(want_matrix, got_matrix); - } - - /// `operator_sum -= matrix_operator` - { - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); - operator_sum -= cudaq::matrix_operator::annihilate(0); - - ASSERT_TRUE(operator_sum.n_terms() == 3); - - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); - auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::id_matrix(level_count)); - - auto got_matrix = - operator_sum.to_matrix({{0, level_count}, {1, level_count}}); - auto want_matrix = term_0_full + term_1_full - self_full; - utils_0::checkEqual(want_matrix, got_matrix); - } - - /// `operator_sum *= matrix_operator` - { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); - - operator_sum *= self; - - ASSERT_TRUE(operator_sum.n_terms() == 2); - for (auto &term : operator_sum.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); - - auto self_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::annihilate_matrix(level_count)); - auto term_0_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::create_matrix(level_count)); - auto term_1_full = cudaq::kronecker(utils_0::id_matrix(level_count), - utils_0::id_matrix(level_count)); - auto sum_full = term_0_full + term_1_full; - - auto got_matrix = - operator_sum.to_matrix({{0, level_count}, {1, level_count}}); - auto want_matrix = sum_full * self_full; - utils_0::checkEqual(want_matrix, got_matrix); - } -} diff --git a/unittests/dynamics/matrix_ops_simple.cpp b/unittests/dynamics/matrix_ops_simple.cpp deleted file mode 100644 index 382cb24be9..0000000000 --- a/unittests/dynamics/matrix_ops_simple.cpp +++ /dev/null @@ -1,249 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/operators.h" -#include "cudaq/dynamics/matrix_operators.h" -#include - -namespace utils { -void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { - ASSERT_EQ(a.get_rank(), b.get_rank()); - ASSERT_EQ(a.get_rows(), b.get_rows()); - ASSERT_EQ(a.get_columns(), b.get_columns()); - ASSERT_EQ(a.get_size(), b.get_size()); - for (std::size_t i = 0; i < a.get_rows(); i++) { - for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_real = a[{i, j}].real(); - double b_real = b[{i, j}].real(); - EXPECT_NEAR(a_real, b_real, 1e-8); - double a_imag = a[{i, j}].imag(); - double b_imag = b[{i, j}].imag(); - EXPECT_NEAR(a_imag, b_imag, 1e-8); - } - } -} - -cudaq::matrix_2 zero_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - return mat; -} - -cudaq::matrix_2 id_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = 1.0 + 0.0j; - return mat; -} - -cudaq::matrix_2 annihilate_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 create_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 position_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 momentum_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = - (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = - -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 number_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = static_cast(i) + 0.0j; - return mat; -} - -cudaq::matrix_2 parity_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; - return mat; -} - -cudaq::matrix_2 displace_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = amplitude * create_matrix(size); - auto term2 = std::conj(amplitude) * annihilate_matrix(size); - auto difference = term1 - term2; - return difference.exponential(); -} - -cudaq::matrix_2 squeeze_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); - auto term2 = amplitude * create_matrix(size).power(2); - auto difference = 0.5 * (term1 - term2); - return difference.exponential(); -} - -} // namespace utils - -TEST(OperatorExpressions, checkPreBuiltElementaryOps) { - std::vector levels = {2, 3, 4, 5}; - - // Keeping this fixed throughout. - int degree_index = 0; - - // Identity operator. - { - for (auto level_count : levels) { - auto id = cudaq::matrix_operator::identity(degree_index); - auto got_id = id.to_matrix({{degree_index, level_count}}); - auto want_id = utils::id_matrix(level_count); - utils::checkEqual(want_id, got_id); - } - } - - // Zero operator. - { - for (auto level_count : levels) { - auto zero = cudaq::matrix_operator::zero(degree_index); - auto got_zero = zero.to_matrix({{degree_index, level_count}}); - auto want_zero = utils::zero_matrix(level_count); - utils::checkEqual(want_zero, got_zero); - } - } - - // Annihilation operator. - { - for (auto level_count : levels) { - auto annihilate = cudaq::matrix_operator::annihilate(degree_index); - auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); - auto want_annihilate = utils::annihilate_matrix(level_count); - utils::checkEqual(want_annihilate, got_annihilate); - } - } - - // Creation operator. - { - for (auto level_count : levels) { - auto create = cudaq::matrix_operator::create(degree_index); - auto got_create = create.to_matrix({{degree_index, level_count}}); - auto want_create = utils::create_matrix(level_count); - utils::checkEqual(want_create, got_create); - } - } - - // Position operator. - { - for (auto level_count : levels) { - auto position = cudaq::matrix_operator::position(degree_index); - auto got_position = position.to_matrix({{degree_index, level_count}}); - auto want_position = utils::position_matrix(level_count); - utils::checkEqual(want_position, got_position); - } - } - - // Momentum operator. - { - for (auto level_count : levels) { - auto momentum = cudaq::matrix_operator::momentum(degree_index); - auto got_momentum = momentum.to_matrix({{degree_index, level_count}}); - auto want_momentum = utils::momentum_matrix(level_count); - utils::checkEqual(want_momentum, got_momentum); - } - } - - // Number operator. - { - for (auto level_count : levels) { - auto number = cudaq::matrix_operator::number(degree_index); - auto got_number = number.to_matrix({{degree_index, level_count}}); - auto want_number = utils::number_matrix(level_count); - utils::checkEqual(want_number, got_number); - } - } - - // Parity operator. - { - for (auto level_count : levels) { - auto parity = cudaq::matrix_operator::parity(degree_index); - auto got_parity = parity.to_matrix({{degree_index, level_count}}); - auto want_parity = utils::parity_matrix(level_count); - utils::checkEqual(want_parity, got_parity); - } - } - - // Displacement operator. - { - for (auto level_count : levels) { - auto displacement = 2.0 + 1.0j; - auto displace = cudaq::matrix_operator::displace(degree_index); - auto got_displace = displace.to_matrix({{degree_index, level_count}}, - {{"displacement", displacement}}); - auto want_displace = utils::displace_matrix(level_count, displacement); - utils::checkEqual(want_displace, got_displace); - } - } - - // Squeeze operator. - { - for (auto level_count : levels) { - auto squeezing = 2.0 + 1.0j; - auto squeeze = cudaq::matrix_operator::squeeze(degree_index); - auto got_squeeze = squeeze.to_matrix({{degree_index, level_count}}, - {{"squeezing", squeezing}}); - auto want_squeeze = utils::squeeze_matrix(level_count, squeezing); - utils::checkEqual(want_squeeze, got_squeeze); - } - } -} - -TEST(OperatorExpressions, checkCustomElementaryOps) { - auto level_count = 2; - std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}}; - - { - auto func0 = [](std::vector dimensions, - std::map> _none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](std::vector dimensions, - std::map> _none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } - - auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); - auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); - - auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), - utils::position_matrix(level_count + 2)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), - utils::number_matrix(level_count)); - - utils::checkEqual(op0.to_matrix(dimensions), matrix0); - utils::checkEqual(op1.to_matrix(dimensions), matrix1); -} diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 817359f7b3..6953561e9b 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -6,107 +6,10 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "utils.h" #include "cudaq/operators.h" -#include "cudaq/dynamics/matrix_operators.h" #include -namespace utils_2 { -void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { - ASSERT_EQ(a.get_rank(), b.get_rank()); - ASSERT_EQ(a.get_rows(), b.get_rows()); - ASSERT_EQ(a.get_columns(), b.get_columns()); - ASSERT_EQ(a.get_size(), b.get_size()); - for (std::size_t i = 0; i < a.get_rows(); i++) { - for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_real = a[{i, j}].real(); - double b_real = b[{i, j}].real(); - EXPECT_NEAR(a_real, b_real, 1e-8); - double a_imag = a[{i, j}].imag(); - double b_imag = b[{i, j}].imag(); - EXPECT_NEAR(a_imag, b_imag, 1e-8); - } - } -} - -cudaq::matrix_2 zero_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - return mat; -} - -cudaq::matrix_2 id_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = 1.0 + 0.0j; - return mat; -} - -cudaq::matrix_2 annihilate_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 create_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 position_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 momentum_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = - (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = - -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 number_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = static_cast(i) + 0.0j; - return mat; -} - -cudaq::matrix_2 parity_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; - return mat; -} - -cudaq::matrix_2 displace_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = amplitude * create_matrix(size); - auto term2 = std::conj(amplitude) * annihilate_matrix(size); - auto difference = term1 - term2; - return difference.exponential(); -} - -cudaq::matrix_2 squeeze_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); - auto term2 = amplitude * create_matrix(size).power(2); - auto difference = 0.5 * (term1 - term2); - return difference.exponential(); -} - -} // namespace utils_2 - - TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { int level_count = 3; std::complex value = 0.2 + 0.2j; @@ -137,18 +40,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` @@ -167,18 +70,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` @@ -197,18 +100,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= scalar_operator` @@ -228,18 +131,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)}; std::vector matrices_2 = { - utils_2::momentum_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = - value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) * scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum += scalar_operator` @@ -255,18 +157,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils_2::id_matrix(level_count + 1), - utils_2::parity_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; std::vector matrices_2 = { - utils_2::position_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::position_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = - value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); auto want_matrix = matrix0 + matrix1 + scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum -= scalar_operator` @@ -282,18 +183,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils_2::id_matrix(level_count + 1), - utils_2::number_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::number_matrix(level_count)}; std::vector matrices_2 = { - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = - value * utils_2::id_matrix((level_count + 1) * level_count); + auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) - scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -330,18 +230,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + double` and `double + operator_sum` @@ -359,18 +259,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::momentum_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::position_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::momentum_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - double` and `double - operator_sum` @@ -389,18 +289,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::parity_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::number_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= double` @@ -421,17 +321,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{"squeezing", value}}); auto matrix0 = - cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::squeeze_matrix(level_count, value)); + cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::squeeze_matrix(level_count, value)); auto matrix1 = - cudaq::kronecker(utils_2::squeeze_matrix(level_count + 1, value), - utils_2::id_matrix(level_count)); + cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum += double` @@ -445,17 +345,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum -= double` @@ -469,17 +369,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - double_value * utils_2::id_matrix((level_count) * (level_count + 1)); + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum * std::complex` and `std::complex * @@ -509,18 +409,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; auto want_matrix_reverse = scaled_identity * sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum + std::complex` and `std::complex + @@ -539,18 +439,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; auto want_matrix_reverse = scaled_identity + sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum - std::complex` and `std::complex - @@ -570,18 +470,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= std::complex` @@ -601,16 +501,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{"displacement", value}}); auto matrix0 = - cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::displace_matrix(level_count, value)); - auto matrix1 = cudaq::kronecker(utils_2::parity_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::displace_matrix(level_count, value)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum += std::complex` @@ -625,17 +525,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, {{"squeezing", value}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::momentum_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::momentum_matrix(level_count)); auto matrix1 = - cudaq::kronecker(utils_2::squeeze_matrix(level_count + 1, value), - utils_2::id_matrix(level_count)); + cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix + scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum -= std::complex` @@ -649,16 +549,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils_2::id_matrix(level_count + 1), - utils_2::position_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils_2::number_matrix(level_count + 1), - utils_2::id_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::position_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), + utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = - value * utils_2::id_matrix((level_count) * (level_count + 1)); + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -688,26 +588,26 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_0_1 = {utils_2::id_matrix(level_count + 3), - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::parity_matrix(level_count)}; - matrices_1_1 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_2 = {utils_2::create_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 3), + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; + matrices_1_1 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_2 = {utils::create_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto sum_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + @@ -718,7 +618,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix + sum_1_matrix; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum - operator_sum` @@ -744,26 +644,26 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_0_1 = {utils_2::id_matrix(level_count + 3), - utils_2::position_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::parity_matrix(level_count)}; - matrices_1_1 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_2 = {utils_2::momentum_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 3), + utils::position_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; + matrices_1_1 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_2 = {utils::momentum_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto sum_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + @@ -774,7 +674,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix - sum_1_matrix; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum * operator_sum` @@ -811,26 +711,26 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_0_1 = {utils_2::id_matrix(level_count + 3), - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::parity_matrix(level_count)}; - matrices_1_1 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_2 = {utils_2::create_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 3), + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; + matrices_1_1 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_2 = {utils::create_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto sum_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + @@ -842,8 +742,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto want_matrix = sum_0_matrix * sum_1_matrix; auto want_matrix_reverse = sum_1_matrix * sum_0_matrix; - utils_2::checkEqual(want_matrix, got_matrix); - utils_2::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `operator_sum *= operator_sum` @@ -871,26 +771,26 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_0_1 = {utils_2::id_matrix(level_count + 3), - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_0 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::parity_matrix(level_count)}; - matrices_1_1 = {utils_2::id_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; - matrices_1_2 = {utils_2::create_matrix(level_count + 3), - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 3), + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; + matrices_1_1 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; + matrices_1_2 = {utils::create_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto sum_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) + @@ -901,7 +801,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { cudaq::kronecker(matrices_1_2.begin(), matrices_1_2.end()); auto want_matrix = sum_0_matrix * sum_1_matrix; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -925,21 +825,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -949,7 +850,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix + product_matrix; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum -= product_operator` @@ -966,21 +867,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -990,7 +892,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix - product_matrix; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `operator_sum *= product_operator` @@ -1010,21 +912,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils_2::id_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), - utils_2::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { - utils_2::id_matrix(level_count + 2), - utils_2::annihilate_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils_2::id_matrix(level_count + 2), - utils_2::create_matrix(level_count + 1), - utils_2::id_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1), utils_2::id_matrix(level_count)}; + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1034,7 +937,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = sum_matrix * product_matrix; - utils_2::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -1045,13 +948,13 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { { auto func0 = [](std::vector dimensions, std::map> _none) { - return cudaq::kronecker(utils_2::momentum_matrix(dimensions[0]), - utils_2::position_matrix(dimensions[1]));; + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; }; auto func1 = [](std::vector dimensions, std::map> _none) { - return cudaq::kronecker(utils_2::create_matrix(dimensions[0]), - utils_2::number_matrix(dimensions[1]));; + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; }; cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); @@ -1065,13 +968,13 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { auto difference_reverse = op1 - op0; std::vector matrices_0 = { - utils_2::id_matrix(level_count), - utils_2::position_matrix(level_count + 2), - utils_2::momentum_matrix(level_count + 1)}; + utils::id_matrix(level_count), + utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; std::vector matrices_1 = { - utils_2::number_matrix(level_count), - utils_2::create_matrix(level_count + 2), - utils_2::id_matrix(level_count + 1)}; + utils::number_matrix(level_count), + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1)}; auto sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - @@ -1079,10 +982,10 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { auto diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - utils_2::checkEqual(sum.to_matrix(dimensions), sum_expected); - utils_2::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); - utils_2::checkEqual(difference.to_matrix(dimensions), diff_expected); - utils_2::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); + utils::checkEqual(sum.to_matrix(dimensions), sum_expected); + utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); + utils::checkEqual(difference.to_matrix(dimensions), diff_expected); + utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {2, 3})); op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {2, 0})); @@ -1092,13 +995,13 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { difference_reverse = op1 - op0; matrices_0 = { - utils_2::position_matrix(level_count + 3), - utils_2::momentum_matrix(level_count), - utils_2::id_matrix(level_count + 1)}; + utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count), + utils::id_matrix(level_count + 1)}; matrices_1 = { - utils_2::id_matrix(level_count + 3), - utils_2::create_matrix(level_count), - utils_2::number_matrix(level_count + 1)}; + utils::id_matrix(level_count + 3), + utils::create_matrix(level_count), + utils::number_matrix(level_count + 1)}; sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - @@ -1106,8 +1009,8 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - utils_2::checkEqual(sum.to_matrix(dimensions), sum_expected); - utils_2::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); - utils_2::checkEqual(difference.to_matrix(dimensions), diff_expected); - utils_2::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); + utils::checkEqual(sum.to_matrix(dimensions), sum_expected); + utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); + utils::checkEqual(difference.to_matrix(dimensions), diff_expected); + utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); } diff --git a/unittests/dynamics/product_operators_arithmetic.cpp b/unittests/dynamics/product_operator.cpp similarity index 61% rename from unittests/dynamics/product_operators_arithmetic.cpp rename to unittests/dynamics/product_operator.cpp index 5ecc1be87d..a276f36fd9 100644 --- a/unittests/dynamics/product_operators_arithmetic.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -6,120 +6,12 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "utils.h" #include "cudaq/operators.h" -#include "cudaq/dynamics/matrix_operators.h" #include #include -namespace utils_1 { -void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { - ASSERT_EQ(a.get_rank(), b.get_rank()); - ASSERT_EQ(a.get_rows(), b.get_rows()); - ASSERT_EQ(a.get_columns(), b.get_columns()); - ASSERT_EQ(a.get_size(), b.get_size()); - for (std::size_t i = 0; i < a.get_rows(); i++) { - for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_real = a[{i, j}].real(); - double b_real = b[{i, j}].real(); - EXPECT_NEAR(a_real, b_real, 1e-8); - double a_imag = a[{i, j}].imag(); - double b_imag = b[{i, j}].imag(); - EXPECT_NEAR(a_imag, b_imag, 1e-8); - } - } -} - -cudaq::matrix_2 zero_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - return mat; -} - -cudaq::matrix_2 id_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = 1.0 + 0.0j; - return mat; -} - -cudaq::matrix_2 annihilate_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 create_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - return mat; -} - -cudaq::matrix_2 position_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 momentum_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i + 1 < size; i++) { - mat[{i + 1, i}] = - (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = - -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; -} - -cudaq::matrix_2 number_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = static_cast(i) + 0.0j; - return mat; -} - -cudaq::matrix_2 parity_matrix(std::size_t size) { - auto mat = cudaq::matrix_2(size, size); - for (std::size_t i = 0; i < size; i++) - mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; - return mat; -} - -cudaq::matrix_2 displace_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = amplitude * create_matrix(size); - auto term2 = std::conj(amplitude) * annihilate_matrix(size); - auto difference = term1 - term2; - return difference.exponential(); -} - -cudaq::matrix_2 squeeze_matrix(std::size_t size, - std::complex amplitude) { - auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); - auto term2 = amplitude * create_matrix(size).power(2); - auto difference = 0.5 * (term1 - term2); - return difference.exponential(); -} - -void assert_product_equal( - const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { - - auto sumterms_prod = - ((const cudaq::operator_sum &)got).get_terms(); - ASSERT_TRUE(sumterms_prod.size() == 1); - ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); - ASSERT_TRUE(got.get_terms() == expected_terms); -} - -} // namespace utils_1 - TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { std::vector levels = {2, 3, 4}; @@ -131,16 +23,13 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto op1 = cudaq::matrix_operator::create(0); cudaq::product_operator got = op0 * op1; - utils_1::assert_product_equal( - got, 1., - {cudaq::matrix_operator("annihilate", {0}), - cudaq::matrix_operator("create", {0})}); + utils::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {0}), cudaq::matrix_operator("create", {0})}); auto got_matrix = got.to_matrix({{0, level_count}}); - auto matrix0 = utils_1::annihilate_matrix(level_count); - auto matrix1 = utils_1::create_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); auto want_matrix = matrix0 * matrix1; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); std::vector want_degrees = {0}; ASSERT_TRUE(got.degrees() == want_degrees); @@ -164,17 +53,17 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto identity = utils_1::id_matrix(level_count); - auto matrix0 = utils_1::annihilate_matrix(level_count); - auto matrix1 = utils_1::create_matrix(level_count); + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); auto fullHilbert0 = cudaq::kronecker(identity, matrix0); auto fullHilbert1 = cudaq::kronecker(matrix1, identity); auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -196,17 +85,17 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {2, level_count}}); - auto identity = utils_1::id_matrix(level_count); - auto matrix0 = utils_1::annihilate_matrix(level_count); - auto matrix1 = utils_1::create_matrix(level_count); + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); auto fullHilbert0 = cudaq::kronecker(identity, matrix0); auto fullHilbert1 = cudaq::kronecker(matrix1, identity); auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -229,9 +118,9 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto got_matrix_reverse = got_reverse.to_matrix( {{0, level_count}, {1, level_count}, {2, level_count}}); - auto identity = utils_1::id_matrix(level_count); - auto matrix0 = utils_1::annihilate_matrix(level_count); - auto matrix1 = utils_1::create_matrix(level_count); + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); std::vector matrices_0; std::vector matrices_1; @@ -245,8 +134,8 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { auto want_matrix = fullHilbert0 * fullHilbert1; auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(got_matrix, want_matrix); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(got_matrix, want_matrix); } } } @@ -319,19 +208,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + double` @@ -353,18 +241,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator + scalar_operator` @@ -387,19 +275,19 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - complex` @@ -422,19 +310,19 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - double` @@ -457,18 +345,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator - scalar_operator` @@ -492,19 +380,19 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::momentum_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::momentum_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::momentum_matrix(level_count), + utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * complex` @@ -531,19 +419,19 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::number_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::number_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::number_matrix(level_count), + utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * double` @@ -572,18 +460,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::parity_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::parity_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::parity_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::parity_matrix(level_count), + utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; - auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator * scalar_operator` @@ -608,19 +496,19 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix_reverse = reverse.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::position_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::position_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::position_matrix(level_count), + utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator *= complex` @@ -637,17 +525,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::number_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::momentum_matrix(level_count), + utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = product_matrix * scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } /// `product_operator *= double` @@ -665,16 +553,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::create_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::create_matrix(level_count), + utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; - auto scaled_identity = 2.0 * utils_1::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = product_matrix * scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } /// `product_operator *= scalar_operator` @@ -694,16 +582,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - auto term_0 = cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::number_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils_1::momentum_matrix(level_count), - utils_1::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::momentum_matrix(level_count), + utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; auto scaled_identity = - value_0 * utils_1::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = product_matrix * scaled_identity; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -731,21 +619,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils_1::id_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)}; - matrices_0_1 = {utils_1::id_matrix(level_count + 1), - utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), - utils_1::id_matrix(level_count)}; - matrices_1_1 = {utils_1::annihilate_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), + utils::id_matrix(level_count)}; + matrices_1_1 = {utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto term_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -755,7 +643,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix + term_1_matrix; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `product_operator - product_operator` @@ -773,21 +661,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils_1::id_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)}; - matrices_0_1 = {utils_1::id_matrix(level_count + 1), - utils_1::number_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 1), + utils::number_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), - utils_1::id_matrix(level_count)}; - matrices_1_1 = {utils_1::momentum_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), + utils::id_matrix(level_count)}; + matrices_1_1 = {utils::momentum_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto term_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -797,7 +685,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix - term_1_matrix; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `product_operator * product_operator` @@ -815,21 +703,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils_1::id_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::position_matrix(level_count)}; - matrices_0_1 = {utils_1::id_matrix(level_count + 1), - utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::position_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), - utils_1::id_matrix(level_count)}; - matrices_1_1 = {utils_1::parity_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), + utils::id_matrix(level_count)}; + matrices_1_1 = {utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto term_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -839,7 +727,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } // `product_operator *= product_operator` @@ -857,21 +745,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils_1::id_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)}; - matrices_0_1 = {utils_1::id_matrix(level_count + 1), - utils_1::number_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)}; + matrices_0_1 = {utils::id_matrix(level_count + 1), + utils::number_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), - utils_1::id_matrix(level_count)}; - matrices_1_1 = {utils_1::annihilate_matrix(level_count + 1), - utils_1::id_matrix(level_count), - utils_1::id_matrix(level_count)}; + matrices_1_0 = {utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), + utils::id_matrix(level_count)}; + matrices_1_1 = {utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto term_0_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -881,7 +769,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -906,18 +794,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = - cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)) * - cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto elementary_matrix = cudaq::kronecker( - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + utils::create_matrix(level_count), utils::id_matrix(level_count)); auto want_matrix = product_matrix + elementary_matrix; auto want_matrix_reverse = elementary_matrix + product_matrix; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - matrix_operator` @@ -938,18 +826,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = - cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)) * - cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto elementary_matrix = cudaq::kronecker( - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + utils::create_matrix(level_count), utils::id_matrix(level_count)); auto want_matrix = product_matrix - elementary_matrix; auto want_matrix_reverse = elementary_matrix - product_matrix; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * matrix_operator` @@ -969,18 +857,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = - cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)) * - cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto elementary_matrix = cudaq::kronecker( - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + utils::create_matrix(level_count), utils::id_matrix(level_count)); auto want_matrix = product_matrix * elementary_matrix; auto want_matrix_reverse = elementary_matrix * product_matrix; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator *= matrix_operator` @@ -996,16 +884,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto product_matrix = - cudaq::kronecker(utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)) * - cudaq::kronecker(utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)); + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); auto elementary_matrix = cudaq::kronecker( - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)); + utils::create_matrix(level_count), utils::id_matrix(level_count)); auto want_matrix = product_matrix * elementary_matrix; - utils_1::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix); } } @@ -1033,18 +921,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // Cast every term to full Hilbert space. std::vector matrices_0_0 = { - utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { - utils_1::id_matrix(level_count + 1), - utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils_1::create_matrix(level_count + 1), - utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1055,8 +943,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix + sum_matrix; auto want_matrix_reverse = sum_matrix + product_matrix; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - operator_sum` @@ -1077,18 +965,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // Cast every term to full Hilbert space. std::vector matrices_0_0 = { - utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { - utils_1::id_matrix(level_count + 1), - utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils_1::create_matrix(level_count + 1), - utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1099,8 +987,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix - difference_matrix; auto want_matrix_reverse = difference_matrix - product_matrix; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator * operator_sum` @@ -1121,18 +1009,18 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // Cast every term to full Hilbert space. std::vector matrices_0_0 = { - utils_1::id_matrix(level_count + 1), utils_1::id_matrix(level_count), - utils_1::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { - utils_1::id_matrix(level_count + 1), - utils_1::annihilate_matrix(level_count), - utils_1::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils_1::id_matrix(level_count + 1), - utils_1::create_matrix(level_count), utils_1::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::create_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils_1::create_matrix(level_count + 1), - utils_1::id_matrix(level_count), utils_1::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1143,8 +1031,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto want_matrix = product_matrix * sum_matrix; auto want_matrix_reverse = sum_matrix * product_matrix; - utils_1::checkEqual(want_matrix, got_matrix); - utils_1::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } } @@ -1155,13 +1043,13 @@ TEST(OperatorExpressions, checkCustomProductOps) { { auto func0 = [](std::vector dimensions, std::map> _none) { - return cudaq::kronecker(utils_1::momentum_matrix(dimensions[0]), - utils_1::position_matrix(dimensions[1]));; + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; }; auto func1 = [](std::vector dimensions, std::map> _none) { - return cudaq::kronecker(utils_1::create_matrix(dimensions[0]), - utils_1::number_matrix(dimensions[1]));; + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; }; cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); @@ -1173,19 +1061,19 @@ TEST(OperatorExpressions, checkCustomProductOps) { auto reverse = op1 * op0; std::vector matrices = { - utils_1::number_matrix(level_count), - utils_1::position_matrix(level_count + 2) * utils_1::create_matrix(level_count + 2), - utils_1::momentum_matrix(level_count + 1)}; + utils::number_matrix(level_count), + utils::position_matrix(level_count + 2) * utils::create_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; auto expected = cudaq::kronecker(matrices.begin(), matrices.end()); std::vector matrices_reverse = { - utils_1::number_matrix(level_count), - utils_1::create_matrix(level_count + 2) * utils_1::position_matrix(level_count + 2), - utils_1::momentum_matrix(level_count + 1)}; + utils::number_matrix(level_count), + utils::create_matrix(level_count + 2) * utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; auto expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); - utils_1::checkEqual(product.to_matrix(dimensions), expected); - utils_1::checkEqual(reverse.to_matrix(dimensions), expected_reverse); + utils::checkEqual(product.to_matrix(dimensions), expected); + utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {2, 3})); op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {2, 0})); @@ -1193,18 +1081,18 @@ TEST(OperatorExpressions, checkCustomProductOps) { reverse = op1 * op0; matrices = { - utils_1::position_matrix(level_count + 3), - utils_1::momentum_matrix(level_count) * utils_1::create_matrix(level_count), - utils_1::number_matrix(level_count + 1)}; + utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count) * utils::create_matrix(level_count), + utils::number_matrix(level_count + 1)}; expected = cudaq::kronecker(matrices.begin(), matrices.end()); matrices_reverse = { - utils_1::position_matrix(level_count + 3), - utils_1::create_matrix(level_count) * utils_1::momentum_matrix(level_count), - utils_1::number_matrix(level_count + 1)}; + utils::position_matrix(level_count + 3), + utils::create_matrix(level_count) * utils::momentum_matrix(level_count), + utils::number_matrix(level_count + 1)}; expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); - utils_1::checkEqual(product.to_matrix(dimensions), expected); - utils_1::checkEqual(reverse.to_matrix(dimensions), expected_reverse); + utils::checkEqual(product.to_matrix(dimensions), expected); + utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); } diff --git a/unittests/dynamics/scalar_ops_arithmetic.cpp b/unittests/dynamics/scalar_operator.cpp similarity index 80% rename from unittests/dynamics/scalar_ops_arithmetic.cpp rename to unittests/dynamics/scalar_operator.cpp index e1107807f5..36155a2ee5 100644 --- a/unittests/dynamics/scalar_ops_arithmetic.cpp +++ b/unittests/dynamics/scalar_operator.cpp @@ -9,6 +9,124 @@ #include "cudaq/operators.h" #include +cudaq::scalar_operator negate(cudaq::scalar_operator op) { + return -1.0 * op; +} + +TEST(OperatorExpressions, checkScalarOpsUnary) { + auto scalar = cudaq::scalar_operator(1.0); + EXPECT_EQ((-scalar).evaluate(), std::complex(-1.0)); + EXPECT_EQ(negate(scalar).evaluate(), std::complex(-1.0)); +} + +TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { + + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + // From concrete values. + { + auto operator_0 = cudaq::scalar_operator(value_0); + auto operator_1 = cudaq::scalar_operator(value_1); + auto operator_2 = cudaq::scalar_operator(value_2); + auto operator_3 = cudaq::scalar_operator(value_3); + + auto got_value_0 = operator_0.evaluate(); + auto got_value_1 = operator_1.evaluate(); + auto got_value_2 = operator_2.evaluate(); + auto got_value_3 = operator_3.evaluate(); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } + + // From a lambda function. + { + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + std::map> parameter_map; + + auto operator_0 = cudaq::scalar_operator(function); + auto operator_1 = cudaq::scalar_operator(function); + auto operator_2 = cudaq::scalar_operator(function); + auto operator_3 = cudaq::scalar_operator(function); + + parameter_map["value"] = value_0; + auto got_value_0 = operator_0.evaluate(parameter_map); + parameter_map["value"] = value_1; + auto got_value_1 = operator_1.evaluate(parameter_map); + parameter_map["value"] = value_2; + auto got_value_2 = operator_2.evaluate(parameter_map); + parameter_map["value"] = value_3; + auto got_value_3 = operator_3.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } +} + +TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { + + double value_0 = 0.1; + double value_1 = 0.2; + double value_2 = 2.1; + double value_3 = 2.2; + + // From concrete values. + { + auto operator_0 = cudaq::scalar_operator(value_0); + auto operator_1 = cudaq::scalar_operator(value_1); + auto operator_2 = cudaq::scalar_operator(value_2); + auto operator_3 = cudaq::scalar_operator(value_3); + + auto got_value_0 = operator_0.evaluate(); + auto got_value_1 = operator_1.evaluate(); + auto got_value_2 = operator_2.evaluate(); + auto got_value_3 = operator_3.evaluate(); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } + + // From a lambda function. + { + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + std::map> parameter_map; + + auto operator_0 = cudaq::scalar_operator(function); + auto operator_1 = cudaq::scalar_operator(function); + auto operator_2 = cudaq::scalar_operator(function); + auto operator_3 = cudaq::scalar_operator(function); + + parameter_map["value"] = value_0; + auto got_value_0 = operator_0.evaluate(parameter_map); + parameter_map["value"] = value_1; + auto got_value_1 = operator_1.evaluate(parameter_map); + parameter_map["value"] = value_2; + auto got_value_2 = operator_2.evaluate(parameter_map); + parameter_map["value"] = value_3; + auto got_value_3 = operator_3.evaluate(parameter_map); + + EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); + EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); + EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); + EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); + } +} + TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { // Arithmetic overloads against complex doubles. std::complex value_0 = 0.1 + 0.1; diff --git a/unittests/dynamics/scalar_ops_simple.cpp b/unittests/dynamics/scalar_ops_simple.cpp deleted file mode 100644 index 5da3208520..0000000000 --- a/unittests/dynamics/scalar_ops_simple.cpp +++ /dev/null @@ -1,128 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/operators.h" -#include - -cudaq::scalar_operator negate(cudaq::scalar_operator op) { - return -1.0 * op; -} - -TEST(OperatorExpressions, checkScalarOpsUnary) { - auto scalar = cudaq::scalar_operator(1.0); - EXPECT_EQ((-scalar).evaluate(), std::complex(-1.0)); - EXPECT_EQ(negate(scalar).evaluate(), std::complex(-1.0)); -} - -TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { - - std::complex value_0 = 0.1 + 0.1; - std::complex value_1 = 0.1 + 1.0; - std::complex value_2 = 2.0 + 0.1; - std::complex value_3 = 2.0 + 1.0; - - // From concrete values. - { - auto operator_0 = cudaq::scalar_operator(value_0); - auto operator_1 = cudaq::scalar_operator(value_1); - auto operator_2 = cudaq::scalar_operator(value_2); - auto operator_3 = cudaq::scalar_operator(value_3); - - auto got_value_0 = operator_0.evaluate(); - auto got_value_1 = operator_1.evaluate(); - auto got_value_2 = operator_2.evaluate(); - auto got_value_3 = operator_3.evaluate(); - - EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); - EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); - EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); - EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); - } - - // From a lambda function. - { - auto function = [](std::map> parameters) { - return parameters["value"]; - }; - - std::map> parameter_map; - - auto operator_0 = cudaq::scalar_operator(function); - auto operator_1 = cudaq::scalar_operator(function); - auto operator_2 = cudaq::scalar_operator(function); - auto operator_3 = cudaq::scalar_operator(function); - - parameter_map["value"] = value_0; - auto got_value_0 = operator_0.evaluate(parameter_map); - parameter_map["value"] = value_1; - auto got_value_1 = operator_1.evaluate(parameter_map); - parameter_map["value"] = value_2; - auto got_value_2 = operator_2.evaluate(parameter_map); - parameter_map["value"] = value_3; - auto got_value_3 = operator_3.evaluate(parameter_map); - - EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); - EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); - EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); - EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); - } -} - -TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { - - double value_0 = 0.1; - double value_1 = 0.2; - double value_2 = 2.1; - double value_3 = 2.2; - - // From concrete values. - { - auto operator_0 = cudaq::scalar_operator(value_0); - auto operator_1 = cudaq::scalar_operator(value_1); - auto operator_2 = cudaq::scalar_operator(value_2); - auto operator_3 = cudaq::scalar_operator(value_3); - - auto got_value_0 = operator_0.evaluate(); - auto got_value_1 = operator_1.evaluate(); - auto got_value_2 = operator_2.evaluate(); - auto got_value_3 = operator_3.evaluate(); - - EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); - EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); - EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); - EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); - } - - // From a lambda function. - { - auto function = [](std::map> parameters) { - return parameters["value"]; - }; - - std::map> parameter_map; - - auto operator_0 = cudaq::scalar_operator(function); - auto operator_1 = cudaq::scalar_operator(function); - auto operator_2 = cudaq::scalar_operator(function); - auto operator_3 = cudaq::scalar_operator(function); - - parameter_map["value"] = value_0; - auto got_value_0 = operator_0.evaluate(parameter_map); - parameter_map["value"] = value_1; - auto got_value_1 = operator_1.evaluate(parameter_map); - parameter_map["value"] = value_2; - auto got_value_2 = operator_2.evaluate(parameter_map); - parameter_map["value"] = value_3; - auto got_value_3 = operator_3.evaluate(parameter_map); - - EXPECT_NEAR(std::abs(value_0), std::abs(got_value_0), 1e-5); - EXPECT_NEAR(std::abs(value_1), std::abs(got_value_1), 1e-5); - EXPECT_NEAR(std::abs(value_2), std::abs(got_value_2), 1e-5); - EXPECT_NEAR(std::abs(value_3), std::abs(got_value_3), 1e-5); - } -} \ No newline at end of file diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp new file mode 100644 index 0000000000..dac0837a93 --- /dev/null +++ b/unittests/dynamics/spin_operator.cpp @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "utils.h" +#include "cudaq/operators.h" +#include +#include "cudaq/dynamics/spin_operators.h" + +TEST(OperatorExpressions, checkPreBuiltSpinOps) { + + // Keeping this fixed throughout. + int degree_index = 0; + auto id = utils::id_matrix(2); + + // Identity operator. + { + auto op = cudaq::spin_operator::i(degree_index); + auto got = op.to_matrix(); + auto want = utils::id_matrix(2); + utils::checkEqual(want, got); + utils::checkEqual(id, (op * op).to_matrix()); + } + + // Z operator. + { + auto op = cudaq::spin_operator::z(degree_index); + auto got = op.to_matrix(); + auto want = utils::PauliZ_matrix(); + utils::checkEqual(want, got); + utils::checkEqual(id, (op * op).to_matrix()); + } + + // X operator. + { + auto op = cudaq::spin_operator::x(degree_index); + auto got = op.to_matrix(); + auto want = utils::PauliX_matrix(); + utils::checkEqual(want, got); + utils::checkEqual(id, (op * op).to_matrix()); + } + + // Y operator. + { + auto op = cudaq::spin_operator::y(degree_index); + auto got = op.to_matrix(); + auto want = 1.0j * utils::PauliX_matrix() * utils::PauliZ_matrix(); + utils::checkEqual(want, got); + utils::checkEqual(id, (op * op).to_matrix()); + } +} + diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp new file mode 100644 index 0000000000..71cd29db38 --- /dev/null +++ b/unittests/dynamics/utils.cpp @@ -0,0 +1,141 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/utils/tensor.h" +#include +#include "cudaq/operators.h" +#include "cudaq/dynamics/matrix_operators.h" + +namespace utils { + +void print(cudaq::matrix_2 mat) { + for (std::size_t i = 0; i < mat.get_rows(); i++) { + for (std::size_t j = 0; j < mat.get_columns(); j++) + std::cout << mat[{i, j}] << " "; + std::cout << std::endl; + } +} + +void assert_product_equal(const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { + + auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); + ASSERT_TRUE(sumterms_prod.size() == 1); + ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); + ASSERT_TRUE(got.get_terms() == expected_terms); +} + +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + ASSERT_EQ(a.get_rank(), b.get_rank()); + ASSERT_EQ(a.get_rows(), b.get_rows()); + ASSERT_EQ(a.get_columns(), b.get_columns()); + ASSERT_EQ(a.get_size(), b.get_size()); + for (std::size_t i = 0; i < a.get_rows(); i++) { + for (std::size_t j = 0; j < a.get_columns(); j++) { + double a_real = a[{i, j}].real(); + double b_real = b[{i, j}].real(); + EXPECT_NEAR(a_real, b_real, 1e-8); + double a_imag = a[{i, j}].imag(); + double b_imag = b[{i, j}].imag(); + EXPECT_NEAR(a_imag, b_imag, 1e-8); + } + } +} + +cudaq::matrix_2 zero_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + return mat; +} + +cudaq::matrix_2 id_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = 1.0 + 0.0j; + return mat; +} + +cudaq::matrix_2 annihilate_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 create_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + return mat; +} + +cudaq::matrix_2 position_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 momentum_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i + 1 < size; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; +} + +cudaq::matrix_2 number_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + return mat; +} + +cudaq::matrix_2 parity_matrix(std::size_t size) { + auto mat = cudaq::matrix_2(size, size); + for (std::size_t i = 0; i < size; i++) + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + return mat; +} + +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = amplitude * create_matrix(size); + auto term2 = std::conj(amplitude) * annihilate_matrix(size); + auto difference = term1 - term2; + return difference.exponential(); +} + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude) { + auto term1 = std::conj(amplitude) * annihilate_matrix(size).power(2); + auto term2 = amplitude * create_matrix(size).power(2); + auto difference = 0.5 * (term1 - term2); + return difference.exponential(); +} + +cudaq::matrix_2 PauliX_matrix() { + auto mat = cudaq::matrix_2(2,2); + mat[{0, 1}] = 1.0; + mat[{1, 0}] = 1.0; + return mat; +} + +cudaq::matrix_2 PauliZ_matrix() { + auto mat = cudaq::matrix_2(2,2); + mat[{0, 0}] = 1.0; + mat[{1, 1}] = -1.0; + return mat; +} + +} // namespace utils diff --git a/unittests/dynamics/utils.h b/unittests/dynamics/utils.h new file mode 100644 index 0000000000..d7723ead3c --- /dev/null +++ b/unittests/dynamics/utils.h @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/utils/tensor.h" +#include "cudaq/operators.h" +#include "cudaq/dynamics/matrix_operators.h" + +namespace utils { + +void print(cudaq::matrix_2 mat); + +void assert_product_equal(const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms); + +void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b); + +cudaq::matrix_2 zero_matrix(std::size_t size); + +cudaq::matrix_2 id_matrix(std::size_t size); + +cudaq::matrix_2 annihilate_matrix(std::size_t size); + +cudaq::matrix_2 create_matrix(std::size_t size); + +cudaq::matrix_2 position_matrix(std::size_t size); + +cudaq::matrix_2 momentum_matrix(std::size_t size); + +cudaq::matrix_2 number_matrix(std::size_t size); + +cudaq::matrix_2 parity_matrix(std::size_t size); + +cudaq::matrix_2 displace_matrix(std::size_t size, + std::complex amplitude); + +cudaq::matrix_2 squeeze_matrix(std::size_t size, + std::complex amplitude); + +cudaq::matrix_2 PauliX_matrix(); + +cudaq::matrix_2 PauliZ_matrix(); + +} // namespace utils From 59386f3f4fad3728dfc8f641541be521b5b73d30 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 5 Feb 2025 23:45:25 +0000 Subject: [PATCH 256/311] some more tests, and making dimensions not required for evaluation if the definition makes it clear what the dimension has to be Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/callback.h | 1 + runtime/cudaq/dynamics/matrix_operators.cpp | 36 ++- runtime/cudaq/dynamics/matrix_operators.h | 2 +- runtime/cudaq/dynamics/spin_operators.cpp | 6 +- runtime/cudaq/dynamics/spin_operators.h | 2 +- runtime/cudaq/operators.h | 2 +- unittests/dynamics/matrix_operator.cpp | 13 +- unittests/dynamics/spin_operator.cpp | 303 +++++++++++++++++++- unittests/dynamics/utils.cpp | 4 + unittests/dynamics/utils.h | 2 + 10 files changed, 345 insertions(+), 26 deletions(-) diff --git a/runtime/cudaq/dynamics/callback.h b/runtime/cudaq/dynamics/callback.h index d21e412c56..6ca9ea9da8 100644 --- a/runtime/cudaq/dynamics/callback.h +++ b/runtime/cudaq/dynamics/callback.h @@ -100,6 +100,7 @@ class Definition { std::vector m_expected_dimensions; public: + const std::vector& expected_dimensions = this->m_expected_dimensions; Definition(const std::string &operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create); Definition(Definition &&def); diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 55a2cf977f..e742ca421b 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -73,21 +73,33 @@ matrix_operator& matrix_operator::operator=(matrix_operator &&other) { // evaluations matrix_2 matrix_operator::to_matrix( - std::map dimensions, + std::map &dimensions, std::map> parameters) const { auto it = matrix_operator::m_ops.find(this->id); - if (it != matrix_operator::m_ops.end()) { - std::vector relevant_dimensions; - relevant_dimensions.reserve(this->targets.size()); - for (auto d : this->targets) { - auto entry = dimensions.find(d); - if (entry == dimensions.end()) - throw std::runtime_error("missing dimension for degree " + std::to_string(d)); - relevant_dimensions.push_back(entry->second); - } - return it->second.generate_matrix(relevant_dimensions, parameters); + if (it == matrix_operator::m_ops.end()) + throw std::range_error("unable to find operator"); + + std::vector relevant_dimensions; + relevant_dimensions.reserve(this->targets.size()); + for (auto i = 0; i < this->targets.size(); ++i) { + auto entry = dimensions.find(this->targets[i]); + auto expected_dim = it->second.expected_dimensions[i]; + if (expected_dim <= 0) { + if (entry == dimensions.end()) + throw std::runtime_error("missing dimension for degree " + std::to_string(this->targets[i])); + relevant_dimensions.push_back(entry->second); + } else { + if (entry == dimensions.end()) + dimensions[this->targets[i]] = expected_dim; + else if (entry->second != expected_dim) + throw std::runtime_error("invalid dimension for degree " + + std::to_string(this->targets[i]) + + ", expected dimension is " + std::to_string(expected_dim)); + relevant_dimensions.push_back(expected_dim); + } } - throw std::range_error("unable to find operator"); + + return it->second.generate_matrix(relevant_dimensions, parameters); } // comparisons diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index 1bd1401644..bef53355fd 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -98,7 +98,7 @@ class matrix_operator : operator_handler{ /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map dimensions = {}, + virtual matrix_2 to_matrix(std::map &dimensions, std::map> parameters = {}) const; // comparisons diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index e40d782e5e..95f070af63 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -31,10 +31,12 @@ bool spin_operator::is_identity() const { // evaluations -matrix_2 spin_operator::to_matrix(std::map dimensions, +matrix_2 spin_operator::to_matrix(std::map &dimensions, std::map> parameters) const { auto it = dimensions.find(this->target); - if (it != dimensions.end() && it->second != 2) + if (it == dimensions.end()) + dimensions[this->target] = 2; + else if (it->second != 2) throw std::runtime_error("dimension for spin operator must be 2"); // FIXME: CHECK CONVENTIONS diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 8e0128895f..1e3cf46ed7 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -49,7 +49,7 @@ class spin_operator : operator_handler{ /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map dimensions = {}, + virtual matrix_2 to_matrix(std::map &dimensions, std::map> parameters = {}) const; // comparisons diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 697de8dfb3..90b135cd04 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -514,7 +514,7 @@ class operator_handler { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map dimensions = {}, + virtual matrix_2 to_matrix(std::map &dimensions, std::map> parameters = {}) const = 0; }; diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 89c73d5aaf..49dbf27cd9 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -10,7 +10,7 @@ #include "cudaq/operators.h" #include -TEST(OperatorExpressions, checkElementaryUnary) { +TEST(OperatorExpressions, checkMatrixOpsUnary) { auto create = cudaq::matrix_operator::create(0); utils::checkEqual((-create).to_matrix({{0,2}}), -1.0 * utils::create_matrix(2)); } @@ -147,7 +147,7 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { utils::checkEqual(op1.to_matrix(dimensions), matrix1); } -TEST(OperatorExpressions, checkElementaryAgainstDouble) { +TEST(OperatorExpressions, checkMatrixOpsWithComplex) { std::complex value = 0.125 + 0.125j; // `matrix_operator` + `complex` and `complex` + @@ -207,7 +207,7 @@ TEST(OperatorExpressions, checkElementaryAgainstDouble) { } } -TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { +TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto function = [](std::map> parameters) { return parameters["value"]; @@ -352,8 +352,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsScalars) { } } -/// Prebuilt elementary ops against one another. -TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { +TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { /// Keeping this fixed throughout. int level_count = 3; @@ -462,9 +461,7 @@ TEST(OperatorExpressions, checkPreBuiltElementaryOpsSelf) { } } -/// Testing arithmetic between elementary operators and operator -/// sums. -TEST(OperatorExpressions, checkElementaryOpsAgainstOpSum) { +TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { /// Keeping this fixed throughout. int level_count = 3; diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp index dac0837a93..41224b7f7f 100644 --- a/unittests/dynamics/spin_operator.cpp +++ b/unittests/dynamics/spin_operator.cpp @@ -11,6 +11,11 @@ #include #include "cudaq/dynamics/spin_operators.h" +TEST(OperatorExpressions, checkSpinOpsUnary) { + auto op = cudaq::spin_operator::x(0); + utils::checkEqual((-op).to_matrix(), -1.0 * utils::PauliX_matrix()); +} + TEST(OperatorExpressions, checkPreBuiltSpinOps) { // Keeping this fixed throughout. @@ -48,9 +53,305 @@ TEST(OperatorExpressions, checkPreBuiltSpinOps) { { auto op = cudaq::spin_operator::y(degree_index); auto got = op.to_matrix(); - auto want = 1.0j * utils::PauliX_matrix() * utils::PauliZ_matrix(); + auto want = utils::PauliY_matrix(); utils::checkEqual(want, got); utils::checkEqual(id, (op * op).to_matrix()); } } +TEST(OperatorExpressions, checkSpinOpsWithComplex) { + std::complex value = 0.125 + 0.125j; + + // `spin_operator` + `complex` and `complex` + + // `spin_operator` + { + auto elementary = cudaq::spin_operator::y(0); + + auto sum = value + elementary; + auto reverse = elementary + value; + + auto got_matrix = sum.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto scaled_identity = value * utils::id_matrix(2); + auto want_matrix = scaled_identity + utils::PauliY_matrix(); + auto want_matrix_reverse = utils::PauliY_matrix() + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `spin_operator` - `complex` and `complex` - `spin_operator` + { + auto elementary = cudaq::spin_operator::x(0); + + auto difference = value - elementary; + auto reverse = elementary - value; + + auto got_matrix = difference.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto scaled_identity = value * utils::id_matrix(2); + auto want_matrix = scaled_identity - utils::PauliX_matrix(); + auto want_matrix_reverse = utils::PauliX_matrix() - scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `spin_operator` * `complex` and `complex` * + // `spin_operator` + { + auto elementary = cudaq::spin_operator::z(0); + + auto product = value * elementary; + auto reverse = elementary * value; + + auto got_matrix = product.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto scaled_identity = value * utils::id_matrix(2); + auto want_matrix = scaled_identity * utils::PauliZ_matrix(); + auto want_matrix_reverse = utils::PauliZ_matrix() * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + +TEST(OperatorExpressions, checkSpinOpsWithScalars) { + + auto function = [](std::map> parameters) { + return parameters["value"]; + }; + + /// Keeping these fixed for these more simple tests. + int degree_index = 0; + double const_scale_factor = 2.0; + + // `spin_operator + scalar_operator` + { + auto self = cudaq::spin_operator::x(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(2); + auto got_matrix = sum.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + auto want_matrix = utils::PauliX_matrix() + scaled_identity; + auto want_reverse_matrix = scaled_identity + utils::PauliX_matrix(); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `spin_operator + scalar_operator` + { + auto self = cudaq::spin_operator::y(0); + auto other = cudaq::scalar_operator(function); + + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(2); + auto got_matrix = sum.to_matrix({}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({}, {{"value", const_scale_factor}}); + auto want_matrix = utils::PauliY_matrix() + scaled_identity; + auto want_reverse_matrix = scaled_identity + utils::PauliY_matrix(); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `spin_operator - scalar_operator` + { + auto self = cudaq::spin_operator::i(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(2); + auto got_matrix = sum.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + auto want_matrix = utils::id_matrix(2) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils::id_matrix(2); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `spin_operator - scalar_operator` + { + auto self = cudaq::spin_operator::z(0); + auto other = cudaq::scalar_operator(function); + + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(2); + auto got_matrix = sum.to_matrix({}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({}, {{"value", const_scale_factor}}); + auto want_matrix = utils::PauliZ_matrix() - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils::PauliZ_matrix(); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `spin_operator * scalar_operator` + { + auto self = cudaq::spin_operator::y(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto product = self * other; + auto reverse = other * self; + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto scaled_identity = const_scale_factor * utils::id_matrix(2); + auto got_matrix = product.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + auto want_matrix = utils::PauliY_matrix() * scaled_identity; + auto want_reverse_matrix = scaled_identity * utils::PauliY_matrix(); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `spin_operator * scalar_operator` + { + auto self = cudaq::spin_operator::z(0); + auto other = cudaq::scalar_operator(function); + + auto product = self * other; + auto reverse = other * self; + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto scaled_identity = const_scale_factor * utils::id_matrix(2); + auto got_matrix = product.to_matrix({}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({}, {{"value", const_scale_factor}}); + auto want_matrix = utils::PauliZ_matrix() * scaled_identity; + auto want_reverse_matrix = scaled_identity * utils::PauliZ_matrix(); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } +} + +TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { + + // Addition, same DOF. + { + auto self = cudaq::spin_operator::x(0); + auto other = cudaq::spin_operator::y(0); + + auto sum = self + other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto got_matrix = sum.to_matrix(); + auto want_matrix = utils::PauliX_matrix() + + utils::PauliY_matrix(); + utils::checkEqual(want_matrix, got_matrix); + } + + // Addition, different DOF's. + { + auto self = cudaq::spin_operator::z(0); + auto other = cudaq::spin_operator::y(1); + + auto sum = self + other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto matrix_self = cudaq::kronecker(utils::id_matrix(2), + utils::PauliZ_matrix()); + auto matrix_other = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto got_matrix = sum.to_matrix(); + auto want_matrix = matrix_self + matrix_other; + utils::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, same DOF. + { + auto self = cudaq::spin_operator::z(0); + auto other = cudaq::spin_operator::x(0); + + auto sum = self - other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto got_matrix = sum.to_matrix(); + auto want_matrix = utils::PauliZ_matrix() - + utils::PauliX_matrix(); + utils::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, different DOF's. + { + auto self = cudaq::spin_operator::y(0); + auto other = cudaq::spin_operator::x(1); + + auto sum = self - other; + ASSERT_TRUE(sum.n_terms() == 2); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(2), + utils::PauliY_matrix()); + auto create_full = cudaq::kronecker(utils::PauliX_matrix(), + utils::id_matrix(2)); + auto got_matrix = sum.to_matrix(); + auto want_matrix = annihilate_full - create_full; + utils::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, same DOF. + { + auto self = cudaq::spin_operator::y(0); + auto other = cudaq::spin_operator::z(0); + + auto product = self * other; + ASSERT_TRUE(product.n_terms() == 2); + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto got_matrix = product.to_matrix(); + auto want_matrix = utils::PauliY_matrix() * + utils::PauliZ_matrix(); + utils::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, different DOF's. + { + auto self = cudaq::spin_operator::x(0); + auto other = cudaq::spin_operator::z(1); + + auto product = self * other; + ASSERT_TRUE(product.n_terms() == 2); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto create_full = cudaq::kronecker(utils::PauliZ_matrix(), + utils::id_matrix(2)); + auto got_matrix = product.to_matrix(); + auto want_matrix = annihilate_full * create_full; + utils::checkEqual(want_matrix, got_matrix); + } +} diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp index 71cd29db38..ebf7fbf121 100644 --- a/unittests/dynamics/utils.cpp +++ b/unittests/dynamics/utils.cpp @@ -138,4 +138,8 @@ cudaq::matrix_2 PauliZ_matrix() { return mat; } +cudaq::matrix_2 PauliY_matrix() { + return 1.0j * utils::PauliX_matrix() * utils::PauliZ_matrix(); +} + } // namespace utils diff --git a/unittests/dynamics/utils.h b/unittests/dynamics/utils.h index d7723ead3c..fa04383d56 100644 --- a/unittests/dynamics/utils.h +++ b/unittests/dynamics/utils.h @@ -46,4 +46,6 @@ cudaq::matrix_2 PauliX_matrix(); cudaq::matrix_2 PauliZ_matrix(); +cudaq::matrix_2 PauliY_matrix(); + } // namespace utils From 7a1aefe6ae09d284e8c1e393004e18bf46bc5019 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 6 Feb 2025 13:48:08 +0000 Subject: [PATCH 257/311] more spin ops tests Signed-off-by: Bettina Heim --- unittests/dynamics/matrix_operator.cpp | 83 +- unittests/dynamics/operator_sum.cpp | 1260 ++++++++++++++--------- unittests/dynamics/product_operator.cpp | 950 ++++++++++++----- unittests/dynamics/spin_operator.cpp | 173 ++++ 4 files changed, 1769 insertions(+), 697 deletions(-) diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 49dbf27cd9..e8dc5fc917 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -118,7 +118,7 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { TEST(OperatorExpressions, checkCustomMatrixOps) { auto level_count = 2; - std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}}; + std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; { auto func0 = [](std::vector dimensions, @@ -135,16 +135,52 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } + // op 0: + // momentum level+1 on 0 + // position level+2 on 1 + // op 1: + // number level on 3 + // create level+2 on 1 auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); - auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); + auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 3})); auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), utils::position_matrix(level_count + 2)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), utils::number_matrix(level_count)); - utils::checkEqual(op0.to_matrix(dimensions), matrix0); - utils::checkEqual(op1.to_matrix(dimensions), matrix1); + std::vector product_matrices = { + utils::number_matrix(level_count), + utils::position_matrix(level_count + 2) * utils::create_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1) + }; + std::vector product_reverse_matrices = { + utils::number_matrix(level_count), + utils::create_matrix(level_count + 2) * utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1) + }; + std::vector sum_matrices_term0 = { + utils::id_matrix(level_count), + utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1) + }; + std::vector sum_matrices_term1 = { + utils::number_matrix(level_count), + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1) + }; + + auto expected_product = cudaq::kronecker(product_matrices.begin(), product_matrices.end()); + auto expected_product_reverse = cudaq::kronecker(product_reverse_matrices.begin(), product_reverse_matrices.end()); + auto expected_sum_term0 = cudaq::kronecker(sum_matrices_term0.begin(), sum_matrices_term0.end()); + auto expected_sum_term1 = cudaq::kronecker(sum_matrices_term1.begin(), sum_matrices_term1.end()); + + utils::checkEqual(op0.to_matrix(dimensions), matrix0); // *not* in canonical order; order as defined in custom op definition + utils::checkEqual(op1.to_matrix(dimensions), matrix1); // *not* in canonical order; order as defined in custom op definition + utils::checkEqual((op0 * op1).to_matrix(dimensions), expected_product); // now reordered in canonical order + utils::checkEqual((op1 * op0).to_matrix(dimensions), expected_product_reverse); // now reordered in canonical order + utils::checkEqual((op0 + op1).to_matrix(dimensions), expected_sum_term0 + expected_sum_term1); // now reordered in canonical order + utils::checkEqual((op1 + op0).to_matrix(dimensions), expected_sum_term0 + expected_sum_term1); // now reordered in canonical order } TEST(OperatorExpressions, checkMatrixOpsWithComplex) { @@ -623,3 +659,42 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::checkEqual(want_matrix, got_matrix); } } + +TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { + auto op1 = cudaq::matrix_operator::create(2); + auto op2 = cudaq::matrix_operator::annihilate(0); + std::map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; + + { + auto func0 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](std::vector dimensions, + std::map> _none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto custom_op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {3, 1})); + auto custom_op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 0})); + + ASSERT_THROW(op1.to_matrix(), std::runtime_error); + ASSERT_THROW(op1.to_matrix({{1, 2}}), std::runtime_error); + ASSERT_THROW((op1 * op2).to_matrix({{2, 3}}), std::runtime_error); + ASSERT_THROW((op1 + op2).to_matrix({{0, 3}}), std::runtime_error); + ASSERT_NO_THROW((op1 * op2).to_matrix(dimensions)); + ASSERT_NO_THROW((op1 + op2).to_matrix(dimensions)); + + ASSERT_THROW(custom_op0.to_matrix(), std::runtime_error); + ASSERT_THROW(custom_op1.to_matrix({{1, 2}}), std::runtime_error); + ASSERT_THROW((custom_op1 * custom_op0).to_matrix({{0, 2}, {1, 2}}), std::runtime_error); + ASSERT_THROW((custom_op1 + custom_op0).to_matrix({{0, 2}, {1, 2}, {2, 2}}), std::runtime_error); + ASSERT_NO_THROW((custom_op0 * custom_op1).to_matrix(dimensions)); + ASSERT_NO_THROW((custom_op0 + custom_op1).to_matrix(dimensions)); +} + diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 6953561e9b..b81af388b1 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -9,56 +9,334 @@ #include "utils.h" #include "cudaq/operators.h" #include +#include "cudaq/dynamics/spin_operators.h" -TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { - int level_count = 3; - std::complex value = 0.2 + 0.2j; +TEST(OperatorExpressions, checkOperatorSumBasics) { + std::vector levels = {2, 3, 4}; + + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; - // `operator_sum * scalar_operator` and `scalar_operator * operator_sum` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + // Same degrees of freedom. + { + auto spin0 = cudaq::spin_operator::x(5); + auto spin1 = cudaq::spin_operator::z(5); + auto spin_sum = spin0 + spin1; - auto product = sum * cudaq::scalar_operator(value); - auto reverse = cudaq::scalar_operator(value) * sum; + std::vector want_degrees = {5}; + auto spin_matrix = utils::PauliX_matrix() + utils::PauliZ_matrix(); - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == value); + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(5); + auto op1 = cudaq::matrix_operator::create(5); + + auto sum = op0 + op1; + ASSERT_TRUE(sum.degrees() == want_degrees); + + auto got_matrix = sum.to_matrix({{5, level_count}}); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + auto want_matrix = matrix0 + matrix1; + utils::checkEqual(want_matrix, got_matrix); + } } - for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == value); + // Different degrees of freedom. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(1); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {1, 0}; + auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(1); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 + fullHilbert1; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } } - auto got_matrix = - product.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + // Different degrees of freedom, non-consecutive. + // Should produce the same matrices as the above test. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 + fullHilbert1; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + } + + // Different degrees of freedom, non-consecutive but all dimensions + // provided. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + std::map dimensions = {{0, 2},{1, 2},{2, 2}}; + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + std::vector want_degrees = {2, 0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + dimensions = {{0, level_count},{1, level_count},{2, level_count}}; + auto got_matrix = got.to_matrix(dimensions); + auto got_matrix_reverse = got_reverse.to_matrix(dimensions); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + std::vector matrices_0 = {identity, matrix0}; + std::vector matrices_1 = {matrix1, identity}; + + auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 + fullHilbert1; + auto want_matrix_reverse = fullHilbert1 + fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(got_matrix, want_matrix); + } + } + } + + // Scalar Ops against Elementary Ops + { + // matrix operator against constant + { + auto op = cudaq::matrix_operator::annihilate(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::annihilate_matrix(2); + auto scalar_matrix = value_0 * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); + } + + // spin operator against constant + { + auto op = cudaq::spin_operator::x(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = value_0 * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); + } + + // matrix operator against constant from lambda + { + auto op = cudaq::matrix_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(function); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::annihilate_matrix(2); + auto scalar_matrix = scalar_op.evaluate() * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}})); + } + + // spin operator against constant from lambda + { + auto op = cudaq::spin_operator::x(1); + auto scalar_op = cudaq::scalar_operator(function); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = scalar_op.evaluate() * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}})); + } + } +} + +TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { + int level_count = 3; + std::complex value = 0.1 + 0.1j; + double double_value = 0.1; + + // `operator_sum + double` + { + auto original = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::position(2); + + auto sum = original + double_value; + auto reverse = double_value + original; + + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::momentum_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = matrix0 + matrix1 + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + + // `operator_sum + std::complex` + { + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + + auto sum = original + value; + auto reverse = value + original; + + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = matrix0 + matrix1 + scaled_identity; - auto want_matrix = sum_matrix * scaled_identity; - auto want_matrix_reverse = scaled_identity * sum_matrix; utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix, got_matrix_reverse); } - // `operator_sum + scalar_operator` and `scalar_operator + operator_sum` + // `spin sum + std::complex` + { + auto original = cudaq::spin_operator::x(1) + + cudaq::spin_operator::y(2); + + auto sum = original + value; + auto reverse = value + original; + + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = sum.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto matrix1 = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto scaled_identity = value * utils::id_matrix(2 * 2); + auto want_matrix = matrix0 + matrix1 + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + + // `operator_sum + scalar_operator` { level_count = 2; - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -66,9 +344,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(sum.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2,level_count+1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); @@ -84,10 +362,91 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `operator_sum - scalar_operator` and `scalar_operator - operator_sum` + // `operator_sum - double` { - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::number(2); + + auto difference = original - double_value; + auto reverse = double_value - original; + + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `spin sum - double` + { + auto original = cudaq::spin_operator::x(1) + + cudaq::spin_operator::z(2); + + auto difference = original - double_value; + auto reverse = double_value - original; + + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = difference.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto matrix1 = cudaq::kronecker(utils::PauliZ_matrix(), + utils::id_matrix(2)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = double_value * utils::id_matrix(2 * 2); + + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `operator_sum - std::complex` + { + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + + auto difference = original - value; + auto reverse = value - original; + + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix_reverse = scaled_identity - sum_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `operator_sum - scalar_operator` + { + auto original = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -95,10 +454,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { ASSERT_TRUE(difference.n_terms() == 3); ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = - difference.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); @@ -114,260 +471,355 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalarOperator) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `operator_sum *= scalar_operator` + // `operator_sum * double` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::momentum(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); - sum *= cudaq::scalar_operator(value); + auto product = sum * double_value; + auto reverse = double_value * sum; - ASSERT_TRUE(sum.n_terms() == 2); - for (auto term : sum.get_terms()) { + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + } + + auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = (matrix0 + matrix1) * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + + // `operator_sum * std::complex` + { + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); + + auto product = sum * value; + auto reverse = value * sum; + + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + for (auto term : product.get_terms()) { ASSERT_TRUE(term.n_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = sum.to_matrix( - {{0, level_count}, {1, level_count}, {2, level_count + 1}}); + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } - std::vector matrices_1 = { - utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)}; - std::vector matrices_2 = { - utils::momentum_matrix(level_count + 1), - utils::id_matrix(level_count)}; - auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); + auto got_matrix = product.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = (matrix0 + matrix1) * scaled_identity; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); } - // `operator_sum += scalar_operator` + // `operator_sum * scalar_operator` { auto sum = - cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::position(2); + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); - sum += cudaq::scalar_operator(value); + auto product = sum * cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) * sum; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); - auto got_matrix = sum.to_matrix( - {{0, level_count}, {1, level_count}, {2, level_count + 1}}); + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } - std::vector matrices_1 = { - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; - std::vector matrices_2 = { - utils::position_matrix(level_count + 1), - utils::id_matrix(level_count)}; - auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } + + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::create_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + auto want_matrix_reverse = scaled_identity * sum_matrix; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `spin sum * scalar_operator` + { + auto sum = cudaq::spin_operator::i(1) + + cudaq::spin_operator::y(2); + + auto product = sum * cudaq::scalar_operator(value); + auto reverse = cudaq::scalar_operator(value) * sum; + + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + for (auto term : product.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } + + for (auto term : reverse.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } + + auto got_matrix = product.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto matrix0 = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(2)); + auto matrix1 = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto scaled_identity = value * utils::id_matrix(2 * 2); + auto want_matrix = (matrix0 + matrix1) * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + + // `operator_sum *= double` + { + auto sum = cudaq::matrix_operator::squeeze(1) + + cudaq::matrix_operator::squeeze(2); + + sum *= double_value; + + ASSERT_TRUE(sum.n_terms() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + } + + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + + auto matrix0 = + cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::squeeze_matrix(level_count, value)); + auto matrix1 = + cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), + utils::id_matrix(level_count)); + auto sum_matrix = matrix0 + matrix1; + auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + + auto want_matrix = sum_matrix * scaled_identity; + utils::checkEqual(want_matrix, got_matrix); + } + + // `spin sum *= double` + { + auto sum = cudaq::spin_operator::y(1) + + cudaq::spin_operator::i(2); + + sum *= double_value; + + ASSERT_TRUE(sum.n_terms() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + } + + auto got_matrix = sum.to_matrix(); + auto matrix0 = cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); + auto matrix1 = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); + auto scaled_identity = double_value * utils::id_matrix(2 * 2); + auto want_matrix = (matrix0 + matrix1) * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + } + + // `operator_sum *= std::complex` + { + auto sum = cudaq::matrix_operator::displace(1) + + cudaq::matrix_operator::parity(2); + + sum *= value; + + ASSERT_TRUE(sum.n_terms() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } + + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"displacement", value}}); + + auto matrix0 = + cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::displace_matrix(level_count, value)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)); + auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = (matrix0 + matrix1) * scaled_identity; - auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); } - // `operator_sum -= scalar_operator` + // `operator_sum *= scalar_operator` { - auto sum = cudaq::matrix_operator::number(1) + - cudaq::matrix_operator::annihilate(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::momentum(2); - sum -= cudaq::scalar_operator(value); + sum *= cudaq::scalar_operator(value); - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.n_terms() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.get_coefficient().evaluate() == value); + } auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { utils::id_matrix(level_count + 1), - utils::number_matrix(level_count)}; + utils::create_matrix(level_count)}; std::vector matrices_2 = { - utils::annihilate_matrix(level_count + 1), + utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); - auto want_matrix = (matrix0 + matrix1) - scaled_identity; + auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils::checkEqual(want_matrix, got_matrix); } -} - -TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { - int level_count = 3; - std::complex value = 0.1 + 0.1j; - double double_value = 0.1; - // `operator_sum * double` and `double * operator_sum` + // `operator_sum += double` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); - - auto product = sum * double_value; - auto reverse = double_value * sum; + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == - std::complex(double_value)); - } + sum += double_value; - for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == - std::complex(double_value)); - } + ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = - product.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = matrix0 + matrix1 + scaled_identity; - auto want_matrix = sum_matrix * scaled_identity; - auto want_matrix_reverse = scaled_identity * sum_matrix; utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `operator_sum + double` and `double + operator_sum` + // `spin sum += double` { - auto original = cudaq::matrix_operator::momentum(1) + - cudaq::matrix_operator::position(2); - - auto sum = original + double_value; - auto reverse = double_value + original; + auto sum = cudaq::spin_operator::y(1) + + cudaq::spin_operator::y(2); + sum += double_value; ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); - - auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::momentum_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 1), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto got_matrix = sum.to_matrix({{1, 2}, {2, 2}}); + auto matrix0 = cudaq::kronecker(utils::id_matrix(2), + utils::PauliY_matrix()); + auto matrix1 = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto scaled_identity = double_value * utils::id_matrix(2 * 2); + auto want_matrix = matrix0 + matrix1 + scaled_identity; - auto want_matrix = sum_matrix + scaled_identity; - auto want_matrix_reverse = scaled_identity + sum_matrix; utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `operator_sum - double` and `double - operator_sum` + // `operator_sum += std::complex` { - auto original = - cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::number(2); + auto sum = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::squeeze(2); - auto difference = original - double_value; - auto reverse = double_value - original; + sum += value; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = - difference.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - double_value * utils::id_matrix((level_count) * (level_count + 1)); - - auto want_matrix = sum_matrix - scaled_identity; - auto want_matrix_reverse = scaled_identity - sum_matrix; - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `operator_sum *= double` - { - auto sum = - cudaq::matrix_operator::squeeze(1) + cudaq::matrix_operator::squeeze(2); - - sum *= double_value; - - ASSERT_TRUE(sum.n_terms() == 2); - for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == - std::complex(double_value)); - } - - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, - {{"squeezing", value}}); - - auto matrix0 = - cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::squeeze_matrix(level_count, value)); + utils::momentum_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = matrix0 + matrix1 + scaled_identity; - auto want_matrix = sum_matrix * scaled_identity; utils::checkEqual(want_matrix, got_matrix); } - // `operator_sum += double` + // `operator_sum += scalar_operator` { auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::position(2); - sum += double_value; + sum += cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - - auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - double_value * utils::id_matrix((level_count) * (level_count + 1)); + std::vector matrices_1 = { + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; + std::vector matrices_2 = { + utils::position_matrix(level_count + 1), + utils::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); - auto want_matrix = sum_matrix + scaled_identity; + auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); } // `operator_sum -= double` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); sum -= double_value; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); @@ -375,193 +827,217 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; utils::checkEqual(want_matrix, got_matrix); } - // `operator_sum * std::complex` and `std::complex * - // operator_sum` + // `operator_sum -= std::complex` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); - - auto product = sum * value; - auto reverse = value * sum; + auto sum = cudaq::matrix_operator::position(1) + + cudaq::matrix_operator::number(2); - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == value); - } + sum -= value; - for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == value); - } + ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = - product.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::position_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto want_matrix = matrix0 + matrix1 - scaled_identity; - auto want_matrix = sum_matrix * scaled_identity; - auto want_matrix_reverse = scaled_identity * sum_matrix; utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `operator_sum + std::complex` and `std::complex + - // operator_sum` + // `operator_sum -= scalar_operator` { - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::number(1) + + cudaq::matrix_operator::annihilate(2); - auto sum = original + value; - auto reverse = value + original; + sum -= cudaq::scalar_operator(value); ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count}, {2, level_count + 1}}); - auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + std::vector matrices_1 = { + utils::id_matrix(level_count + 1), + utils::number_matrix(level_count)}; + std::vector matrices_2 = { + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); - auto want_matrix = sum_matrix + scaled_identity; - auto want_matrix_reverse = scaled_identity + sum_matrix; + auto want_matrix = (matrix0 + matrix1) - scaled_identity; utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `operator_sum - std::complex` and `std::complex - - // operator_sum` + // `spin sum -= scalar_operator` { - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::spin_operator::z(1) + + cudaq::spin_operator::y(2); - auto difference = original - value; - auto reverse = value - original; - - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + sum -= cudaq::scalar_operator(value); + ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = - difference.to_matrix({{1, level_count}, {2, level_count + 1}}); - auto got_matrix_reverse = - reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix = sum.to_matrix(); - auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + std::vector matrices_1 = {utils::id_matrix(2), utils::PauliZ_matrix()}; + std::vector matrices_2 = {utils::PauliY_matrix(), utils::id_matrix(2)}; + auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); + auto scaled_identity = value * utils::id_matrix(2 * 2); + auto want_matrix = matrix0 + matrix1 - scaled_identity; - auto want_matrix = sum_matrix - scaled_identity; - auto want_matrix_reverse = scaled_identity - sum_matrix; utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } +} - // `operator_sum *= std::complex` +// FIXME: add tests to check sums against elementary + +// FIXME: add tests to combine general sum with spin product +TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { + // NOTE: Much of the simpler arithmetic between the two is tested in the + // product operator test file. This mainly just tests the assignment operators + // between the two types. + int level_count = 2; + + // `operator_sum += product_operator` { - auto sum = - cudaq::matrix_operator::displace(1) + cudaq::matrix_operator::parity(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); - sum *= value; + sum += product; - ASSERT_TRUE(sum.n_terms() == 2); - for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == value); - } + ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, - {{"displacement", value}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + std::vector matrices_0_0 = { + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; - auto matrix0 = - cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::displace_matrix(level_count, value)); - auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + std::vector matrices_1_0 = { + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; - auto want_matrix = sum_matrix * scaled_identity; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix + product_matrix; utils::checkEqual(want_matrix, got_matrix); } - // `operator_sum += std::complex` + // `operator_sum -= product_operator` { - auto sum = cudaq::matrix_operator::momentum(1) + - cudaq::matrix_operator::squeeze(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); - sum += value; + sum -= product; ASSERT_TRUE(sum.n_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, - {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + std::vector matrices_0_0 = { + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; - auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::momentum_matrix(level_count)); - auto matrix1 = - cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + std::vector matrices_1_0 = { + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; - auto want_matrix = sum_matrix + scaled_identity; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = sum_matrix - product_matrix; utils::checkEqual(want_matrix, got_matrix); } - // `operator_sum -= std::complex` + // `operator_sum *= product_operator` { - auto sum = - cudaq::matrix_operator::position(1) + cudaq::matrix_operator::number(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto sum = cudaq::matrix_operator::create(1) + + cudaq::matrix_operator::create(2); - sum -= value; + sum *= product; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.n_terms() == 2); + for (auto term : sum.get_terms()) { + ASSERT_TRUE(term.n_terms() == 3); + } + + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + std::vector matrices_0_0 = { + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::annihilate_matrix(level_count)}; + std::vector matrices_0_1 = { + utils::id_matrix(level_count + 2), + utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count)}; - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + std::vector matrices_1_0 = { + utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), + utils::id_matrix(level_count)}; + std::vector matrices_1_1 = { + utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; - auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::position_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)); - auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = - value * utils::id_matrix((level_count) * (level_count + 1)); + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); - auto want_matrix = sum_matrix - scaled_identity; + auto want_matrix = sum_matrix * product_matrix; utils::checkEqual(want_matrix, got_matrix); } } +// FIXME: add tests to combine general sum with spin sum TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { int level_count = 2; @@ -805,142 +1281,6 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { } } -/// NOTE: Much of the simpler arithmetic between the two is tested in the -/// product operator test file. This mainly just tests the assignment operators -/// between the two types. -TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { - int level_count = 2; - - // `operator_sum += product_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); - - sum += product; - - ASSERT_TRUE(sum.n_terms() == 3); - - auto got_matrix = sum.to_matrix( - {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); - std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count)}; - std::vector matrices_0_1 = { - utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), - utils::id_matrix(level_count)}; - - std::vector matrices_1_0 = { - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; - std::vector matrices_1_1 = { - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; - - auto product_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); - auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + - cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); - - auto want_matrix = sum_matrix + product_matrix; - utils::checkEqual(want_matrix, got_matrix); - } - - // `operator_sum -= product_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); - - sum -= product; - - ASSERT_TRUE(sum.n_terms() == 3); - - auto got_matrix = sum.to_matrix( - {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); - std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count)}; - std::vector matrices_0_1 = { - utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), - utils::id_matrix(level_count)}; - - std::vector matrices_1_0 = { - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; - std::vector matrices_1_1 = { - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; - - auto product_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); - auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + - cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); - - auto want_matrix = sum_matrix - product_matrix; - utils::checkEqual(want_matrix, got_matrix); - } - - // `operator_sum *= product_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); - - sum *= product; - - ASSERT_TRUE(sum.n_terms() == 2); - for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 3); - } - - auto got_matrix = sum.to_matrix( - {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); - std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count)}; - std::vector matrices_0_1 = { - utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), - utils::id_matrix(level_count)}; - - std::vector matrices_1_0 = { - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; - std::vector matrices_1_1 = { - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; - - auto product_matrix = - cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * - cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); - auto sum_matrix = - cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + - cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); - - auto want_matrix = sum_matrix * product_matrix; - utils::checkEqual(want_matrix, got_matrix); - } -} - TEST(OperatorExpressions, checkCustomOperatorSum) { auto level_count = 2; std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index a276f36fd9..851133af5a 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -9,35 +9,66 @@ #include "utils.h" #include "cudaq/operators.h" #include +#include "cudaq/dynamics/spin_operators.h" #include -TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { +TEST(OperatorExpressions, checkProductOperatorBasics) { std::vector levels = {2, 3, 4}; + std::complex value_0 = 0.1 + 0.1; + std::complex value_1 = 0.1 + 1.0; + std::complex value_2 = 2.0 + 0.1; + std::complex value_3 = 2.0 + 1.0; + + auto local_variable = true; + auto function = [&](std::map> parameters) { + if (!local_variable) + throw std::runtime_error("Local variable not detected."); + return parameters["value"]; + }; + { // Same degrees of freedom. { + auto spin0 = cudaq::spin_operator::x(5); + auto spin1 = cudaq::spin_operator::z(5); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {5}; + auto spin_matrix = utils::PauliX_matrix() * utils::PauliZ_matrix(); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(0); + auto op0 = cudaq::matrix_operator::annihilate(5); + auto op1 = cudaq::matrix_operator::create(5); - cudaq::product_operator got = op0 * op1; - utils::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {0}), cudaq::matrix_operator("create", {0})}); + auto got = op0 * op1; + utils::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {5}), cudaq::matrix_operator("create", {5})}); + ASSERT_TRUE(got.degrees() == want_degrees); - auto got_matrix = got.to_matrix({{0, level_count}}); + auto got_matrix = got.to_matrix({{5, level_count}}); auto matrix0 = utils::annihilate_matrix(level_count); auto matrix1 = utils::create_matrix(level_count); auto want_matrix = matrix0 * matrix1; utils::checkEqual(want_matrix, got_matrix); - - std::vector want_degrees = {0}; - ASSERT_TRUE(got.degrees() == want_degrees); } } // Different degrees of freedom. { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(1); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {1, 0}; + auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + for (auto level_count : levels) { auto op0 = cudaq::matrix_operator::annihilate(0); auto op1 = cudaq::matrix_operator::create(1); @@ -45,7 +76,6 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {1, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -70,6 +100,16 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Different degrees of freedom, non-consecutive. // Should produce the same matrices as the above test. { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + for (auto level_count : levels) { auto op0 = cudaq::matrix_operator::annihilate(0); auto op1 = cudaq::matrix_operator::create(2); @@ -77,7 +117,6 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; - std::vector want_degrees = {2, 0}; ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); @@ -102,6 +141,17 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { // Different degrees of freedom, non-consecutive but all dimensions // provided. { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + std::map dimensions = {{0, 2},{1, 2},{2, 2}}; + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); + for (auto level_count : levels) { auto op0 = cudaq::matrix_operator::annihilate(0); auto op1 = cudaq::matrix_operator::create(2); @@ -113,10 +163,9 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { ASSERT_TRUE(got.degrees() == want_degrees); ASSERT_TRUE(got_reverse.degrees() == want_degrees); - auto got_matrix = got.to_matrix( - {{0, level_count}, {1, level_count}, {2, level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix( - {{0, level_count}, {1, level_count}, {2, level_count}}); + dimensions = {{0, level_count},{1, level_count},{2, level_count}}; + auto got_matrix = got.to_matrix(dimensions); + auto got_matrix_reverse = got_reverse.to_matrix(dimensions); auto identity = utils::id_matrix(level_count); auto matrix0 = utils::annihilate_matrix(level_count); @@ -139,48 +188,71 @@ TEST(OperatorExpressions, checkProductOperatorSimpleMatrixChecks) { } } } -} - -TEST(OperatorExpressions, checkProductOperatorSimpleContinued) { - - std::complex value_0 = 0.1 + 0.1; - std::complex value_1 = 0.1 + 1.0; - std::complex value_2 = 2.0 + 0.1; - std::complex value_3 = 2.0 + 1.0; - - auto local_variable = true; - auto function = [&](std::map> parameters) { - if (!local_variable) - throw std::runtime_error("Local variable not detected."); - return parameters["value"]; - }; // Scalar Ops against Elementary Ops { - // Annihilation against constant. + // matrix operator against constant { - auto id_op = cudaq::matrix_operator::annihilate(0); + auto op = cudaq::matrix_operator::annihilate(0); auto scalar_op = cudaq::scalar_operator(value_0); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::annihilate_matrix(2); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); + } - auto got = scalar_op * id_op; - auto got_reverse = scalar_op * id_op; + // spin operator against constant + { + auto op = cudaq::spin_operator::x(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto product = scalar_op * op; + auto reverse = op * scalar_op; std::vector want_degrees = {0}; - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix()); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); } - // Annihilation against constant from lambda. + // matrix operator against constant from lambda { - auto id_op = cudaq::matrix_operator::annihilate(1); + auto op = cudaq::matrix_operator::annihilate(1); auto scalar_op = cudaq::scalar_operator(function); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::annihilate_matrix(2); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_op.evaluate() * op_matrix, product.to_matrix({{1, 2}})); + utils::checkEqual(scalar_op.evaluate() * op_matrix, reverse.to_matrix({{1, 2}})); + } - auto got = scalar_op * id_op; - auto got_reverse = scalar_op * id_op; + // spin operator against constant from lambda + { + auto op = cudaq::spin_operator::x(1); + auto scalar_op = cudaq::scalar_operator(function); + auto product = scalar_op * op; + auto reverse = op * scalar_op; std::vector want_degrees = {1}; - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_op.evaluate() * op_matrix, product.to_matrix()); + utils::checkEqual(scalar_op.evaluate() * op_matrix, reverse.to_matrix()); } } } @@ -189,13 +261,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::complex value_0 = 0.1 + 0.1; int level_count = 3; - /// `product_operator + complex` + /// `product_operator + double` { auto product_op = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = value_0 + product_op; - auto reverse = product_op + value_0; + auto sum = 2.0 + product_op; + auto reverse = product_op + 2.0; ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); @@ -204,16 +276,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = value_0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; @@ -222,13 +293,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - /// `product_operator + double` + /// `product_operator + complex` { auto product_op = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = 2.0 + product_op; - auto reverse = product_op + 2.0; + auto sum = value_0 + product_op; + auto reverse = product_op + value_0; ASSERT_TRUE(sum.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); @@ -246,7 +317,39 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = value_0 * utils::id_matrix(level_count * level_count); + + auto want_matrix = scaled_identity + product; + auto want_matrix_reverse = product + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + /// `spin product + complex` + { + auto product_op = cudaq::spin_operator::x(0) * + cudaq::spin_operator::y(1); + + auto sum = value_0 + product_op; + auto reverse = product_op + value_0; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto got_matrix = sum.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto term_0 = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto term_1 = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto product = term_0 * term_1; + auto scaled_identity = value_0 * utils::id_matrix(2 * 2); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; @@ -290,13 +393,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - /// `product_operator - complex` + /// `product_operator - double` { auto product_op = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto difference = value_0 - product_op; - auto reverse = product_op - value_0; + auto difference = 2.0 - product_op; + auto reverse = product_op - 2.0; ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); @@ -305,18 +408,15 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = - difference.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = - value_0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; @@ -325,13 +425,45 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - /// `product_operator - double` + /// `spin product - double` + { + auto product_op = cudaq::spin_operator::i(0) * + cudaq::spin_operator::z(1); + + auto sum = 2.0 - product_op; + auto reverse = product_op - 2.0; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto got_matrix = sum.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + auto term_0 = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(2)); + auto term_1 = cudaq::kronecker(utils::PauliZ_matrix(), + utils::id_matrix(2)); + auto product = term_0 * term_1; + auto scaled_identity = 2.0 * utils::id_matrix(2 * 2); + + auto want_matrix = scaled_identity - product; + auto want_matrix_reverse = product - scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + /// `product_operator - complex` { auto product_op = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto difference = 2.0 - product_op; - auto reverse = product_op - 2.0; + auto difference = value_0 - product_op; + auto reverse = product_op - value_0; ASSERT_TRUE(difference.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); @@ -350,7 +482,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity - product; auto want_matrix_reverse = product - scaled_identity; @@ -395,37 +528,34 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - /// `product_operator * complex` + /// `product_operator * double` { - auto product_op = - cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); + auto product_op = cudaq::matrix_operator::parity(0) * + cudaq::matrix_operator::parity(1); ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == - std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); - auto product = value_0 * product_op; - auto reverse = product_op * value_0; + auto product = 2.0 * product_op; + auto reverse = product_op * 2.0; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); - ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::number_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::number_matrix(level_count), + utils::parity_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::parity_matrix(level_count), utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; - auto scaled_identity = - value_0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; @@ -434,23 +564,21 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - /// `product_operator * double` + /// `product_operator * complex` { auto product_op = - cudaq::matrix_operator::parity(0) * cudaq::matrix_operator::parity(1); + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); ASSERT_TRUE(product_op.n_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); - auto product = 2.0 * product_op; - auto reverse = product_op * 2.0; + auto product = value_0 * product_op; + auto reverse = product_op * value_0; ASSERT_TRUE(product.n_terms() == 2); ASSERT_TRUE(reverse.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == - std::complex(2.)); - ASSERT_TRUE(reverse.get_coefficient().evaluate() == - std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -461,11 +589,12 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::parity_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::parity_matrix(level_count), + utils::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::number_matrix(level_count), utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; - auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; @@ -511,31 +640,40 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - /// `product_operator *= complex` + /// `spin product * scalar_operator` { - auto product = - cudaq::matrix_operator::number(0) * cudaq::matrix_operator::momentum(1); - product *= value_0; + auto product_op = cudaq::spin_operator::z(0) * + cudaq::spin_operator::y(1); + auto scalar_op = cudaq::scalar_operator(value_0); + + auto product = scalar_op * product_op; + auto reverse = product_op * scalar_op; ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); + ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = product.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); - auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::number_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::momentum_matrix(level_count), - utils::id_matrix(level_count)); + auto term_0 = cudaq::kronecker(utils::id_matrix(2), + utils::PauliZ_matrix()); + auto term_1 = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); auto product_matrix = term_0 * term_1; auto scaled_identity = - value_0 * utils::id_matrix(level_count * level_count); + value_0 * utils::id_matrix(2 * 2); - auto want_matrix = product_matrix * scaled_identity; + auto want_matrix = scaled_identity * product_matrix; + auto want_matrix_reverse = product_matrix * scaled_identity; utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } /// `product_operator *= double` @@ -565,6 +703,59 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { utils::checkEqual(want_matrix, got_matrix); } + /// `spin product *= double` + { + auto product = cudaq::spin_operator::y(0) * + cudaq::spin_operator::i(1); + product *= 2.0; + + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto got_matrix = product.to_matrix(); + + auto term_0 = cudaq::kronecker(utils::id_matrix(2), + utils::PauliY_matrix()); + auto term_1 = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(2)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = 2.0 * utils::id_matrix(2 * 2); + + auto want_matrix = product_matrix * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + } + + /// `product_operator *= complex` + { + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::momentum(1); + product *= value_0; + + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + + auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), + utils::number_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::momentum_matrix(level_count), + utils::id_matrix(level_count)); + auto product_matrix = term_0 * term_1; + auto scaled_identity = + value_0 * utils::id_matrix(level_count * level_count); + + auto want_matrix = product_matrix * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + } + /// `product_operator *= scalar_operator` { auto product = @@ -595,18 +786,143 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { } } -TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { +// FIXME: add tests to combine general products with spin ops +TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { int level_count = 3; - std::map dimensions = { - {0, level_count}, {1, level_count}, {2, level_count + 1}}; - // `product_operator + product_operator` + // `product_operator + matrix_operator` { - auto term_0 = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::annihilate(2); + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); + + auto sum = product + elementary; + auto reverse = elementary + product; + + ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto product_matrix = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils::create_matrix(level_count), utils::id_matrix(level_count)); + + auto want_matrix = product_matrix + elementary_matrix; + auto want_matrix_reverse = elementary_matrix + product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `product_operator - matrix_operator` + { + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); + + auto difference = product - elementary; + auto reverse = elementary - product; + + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto product_matrix = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils::create_matrix(level_count), utils::id_matrix(level_count)); + + auto want_matrix = product_matrix - elementary_matrix; + auto want_matrix_reverse = elementary_matrix - product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `product_operator * matrix_operator` + { + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); + + auto product = term_0 * elementary; + auto reverse = elementary * term_0; + + ASSERT_TRUE(product.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto product_matrix = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils::create_matrix(level_count), utils::id_matrix(level_count)); + + auto want_matrix = product_matrix * elementary_matrix; + auto want_matrix_reverse = elementary_matrix * product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `product_operator *= matrix_operator` + { + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::matrix_operator::create(1); + + product *= elementary; + + ASSERT_TRUE(product.n_terms() == 3); + + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + + auto product_matrix = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils::create_matrix(level_count), utils::id_matrix(level_count)); + + auto want_matrix = product_matrix * elementary_matrix; + + utils::checkEqual(want_matrix, got_matrix); + } +} + +// FIXME: add tests to combine general product with spin product +TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { + + int level_count = 3; + std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + + // `product_operator + product_operator` + { + auto term_0 = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::annihilate(2); auto sum = term_0 + term_1; @@ -646,12 +962,57 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { utils::checkEqual(want_matrix, got_matrix); } + // `spin product + spin product` + { + auto term_0 = cudaq::spin_operator::z(0) * + cudaq::spin_operator::y(2); + auto term_1 = cudaq::spin_operator::x(2) * + cudaq::spin_operator::z(4); + + auto sum = term_0 + term_1; + + ASSERT_TRUE(sum.n_terms() == 2); + + std::vector want_degrees = {4, 2, 0}; + ASSERT_TRUE(sum.degrees() == want_degrees); + + auto got_matrix = sum.to_matrix(); + + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils::id_matrix(2), + utils::id_matrix(2), + utils::PauliZ_matrix()}; + matrices_0_1 = {utils::id_matrix(2), + utils::PauliY_matrix(), + utils::id_matrix(2)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils::id_matrix(2), + utils::PauliX_matrix(), + utils::id_matrix(2)}; + matrices_1_1 = {utils::PauliZ_matrix(), + utils::id_matrix(2), + utils::id_matrix(2)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = term_0_matrix + term_1_matrix; + utils::checkEqual(want_matrix, got_matrix); + } + // `product_operator - product_operator` { auto term_0 = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::number(1); - auto term_1 = - cudaq::matrix_operator::create(1) * cudaq::matrix_operator::momentum(2); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -688,12 +1049,53 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { utils::checkEqual(want_matrix, got_matrix); } + // `spin product - spin product` + { + auto term_0 = cudaq::spin_operator::i(0); + auto term_1 = cudaq::spin_operator::x(1) * + cudaq::spin_operator::y(2); + + auto difference = term_0 - term_1; + auto reverse = term_1 - term_0; + + ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto got_matrix = difference.to_matrix(); + auto reverse_matrix = reverse.to_matrix(); + + std::vector matrices_0_0; + matrices_0_0 = {utils::id_matrix(2), + utils::id_matrix(2), + utils::id_matrix(2)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils::id_matrix(2), + utils::PauliX_matrix(), + utils::id_matrix(2)}; + matrices_1_1 = {utils::PauliY_matrix(), + utils::id_matrix(2), + utils::id_matrix(2)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = term_0_matrix - term_1_matrix; + auto want_reverse_matrix = term_1_matrix - term_0_matrix; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, reverse_matrix); + } + // `product_operator * product_operator` { auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::annihilate(1); - auto term_1 = - cudaq::matrix_operator::create(1) * cudaq::matrix_operator::parity(2); + auto term_1 = cudaq::matrix_operator::create(1) * + cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -730,6 +1132,55 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { utils::checkEqual(want_matrix, got_matrix); } + // `spin product * spin product` + { + auto term_0 = cudaq::spin_operator::y(0) * + cudaq::spin_operator::x(1); + auto term_1 = cudaq::spin_operator::z(1) * + cudaq::spin_operator::i(3); + + auto product = term_0 * term_1; + auto reverse = term_1 * term_0; + std::vector expected_degrees = {3, 1, 0}; + + ASSERT_TRUE(product.n_terms() == 4); + ASSERT_TRUE(reverse.n_terms() == 4); + ASSERT_TRUE(product.degrees() == expected_degrees); + + auto got_matrix = product.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils::id_matrix(2), + utils::id_matrix(2), + utils::PauliY_matrix()}; + matrices_0_1 = {utils::id_matrix(2), + utils::PauliX_matrix(), + utils::id_matrix(2)}; + + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils::id_matrix(2), + utils::PauliZ_matrix(), + utils::id_matrix(2)}; + matrices_1_1 = {utils::id_matrix(2), + utils::id_matrix(2), + utils::id_matrix(2)}; + + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = term_0_matrix * term_1_matrix; + auto want_reverse_matrix = term_1_matrix * term_0_matrix; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + // `product_operator *= product_operator` { auto term_0 = cudaq::matrix_operator::annihilate(0) * @@ -769,131 +1220,53 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; + auto term1_only_matrix = cudaq::kronecker(utils::annihilate_matrix(level_count + 1), utils::create_matrix(level_count)); utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(term1_only_matrix, term_1.to_matrix(dimensions)); } -} - -TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { - int level_count = 3; - - // `product_operator + matrix_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); - - auto sum = product + elementary; - auto reverse = elementary + product; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix + elementary_matrix; - auto want_matrix_reverse = elementary_matrix + product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator - matrix_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); - - auto difference = product - elementary; - auto reverse = elementary - product; - - ASSERT_TRUE(difference.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto got_matrix = - difference.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix - elementary_matrix; - auto want_matrix_reverse = elementary_matrix - product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator * matrix_operator` + // `spin product *= spin product` { - auto term_0 = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); + auto term_0 = cudaq::spin_operator::y(3) * + cudaq::spin_operator::y(1); + auto term_1 = cudaq::spin_operator::z(1) * + cudaq::spin_operator::x(0); - auto product = term_0 * elementary; - auto reverse = elementary * term_0; - - ASSERT_TRUE(product.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); - - auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix * elementary_matrix; - auto want_matrix_reverse = elementary_matrix * product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator *= matrix_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); + term_0 *= term_1; - product *= elementary; + ASSERT_TRUE(term_0.n_terms() == 4); - ASSERT_TRUE(product.n_terms() == 3); + auto got_matrix = term_0.to_matrix(); - auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + std::vector matrices_0_0; + std::vector matrices_0_1; + matrices_0_0 = {utils::PauliY_matrix(), + utils::id_matrix(2), + utils::id_matrix(2)}; + matrices_0_1 = {utils::id_matrix(2), + utils::PauliY_matrix(), + utils::id_matrix(2)}; - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); + std::vector matrices_1_0; + std::vector matrices_1_1; + matrices_1_0 = {utils::id_matrix(2), + utils::PauliZ_matrix(), + utils::id_matrix(2)}; + matrices_1_1 = {utils::id_matrix(2), + utils::id_matrix(2), + utils::PauliX_matrix()}; - auto want_matrix = product_matrix * elementary_matrix; + auto term_0_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto term_1_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) * + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + auto want_matrix = term_0_matrix * term_1_matrix; + auto term1_only_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(term1_only_matrix, term_1.to_matrix()); } } @@ -919,7 +1292,6 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto got_matrix = sum.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); - // Cast every term to full Hilbert space. std::vector matrices_0_0 = { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), utils::annihilate_matrix(level_count)}; @@ -947,6 +1319,44 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } + // `spin product + spin sum` + { + auto product = cudaq::spin_operator::x(0) * + cudaq::spin_operator::y(1); + auto original_sum = cudaq::spin_operator::z(1) + + cudaq::spin_operator::i(2); + + auto sum = product + original_sum; + auto reverse = original_sum + product; + + ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = sum.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + std::vector matrices_0_0 = { + utils::id_matrix(2), utils::id_matrix(2), utils::PauliX_matrix()}; + std::vector matrices_0_1 = { + utils::id_matrix(2), utils::PauliY_matrix(), utils::id_matrix(2)}; + std::vector matrices_1_0 = { + utils::id_matrix(2), utils::PauliZ_matrix(), utils::id_matrix(2)}; + std::vector matrices_1_1 = { + utils::id_matrix(2), utils::id_matrix(2), utils::id_matrix(2)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix + sum_matrix; + auto want_matrix_reverse = sum_matrix + product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + // `product_operator - operator_sum` { auto product = cudaq::matrix_operator::annihilate(0) * @@ -963,7 +1373,6 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto got_matrix = difference.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); - // Cast every term to full Hilbert space. std::vector matrices_0_0 = { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), utils::annihilate_matrix(level_count)}; @@ -991,6 +1400,44 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } + // `spin product - spin sum` + { + auto product = cudaq::spin_operator::y(0) * + cudaq::spin_operator::z(1); + auto original_difference = cudaq::spin_operator::x(1) - + cudaq::spin_operator::i(2); + + auto difference = product - original_difference; + auto reverse = original_difference - product; + + ASSERT_TRUE(difference.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto got_matrix = difference.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + std::vector matrices_0_0 = { + utils::id_matrix(2), utils::id_matrix(2), utils::PauliY_matrix()}; + std::vector matrices_0_1 = { + utils::id_matrix(2), utils::PauliZ_matrix(), utils::id_matrix(2)}; + std::vector matrices_1_0 = { + utils::id_matrix(2), utils::PauliX_matrix(), utils::id_matrix(2)}; + std::vector matrices_1_1 = { + utils::id_matrix(2), utils::id_matrix(2), utils::id_matrix(2)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto difference_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) - + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix - difference_matrix; + auto want_matrix_reverse = difference_matrix - product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + // `product_operator * operator_sum` { auto original_product = cudaq::matrix_operator::annihilate(0) * @@ -1007,7 +1454,6 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto got_matrix = product.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); - // Cast every term to full Hilbert space. std::vector matrices_0_0 = { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), utils::annihilate_matrix(level_count)}; @@ -1034,6 +1480,44 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } + + // `spin product * spin sum` + { + auto original_product = cudaq::spin_operator::z(0) * + cudaq::spin_operator::y(1); + auto sum = cudaq::spin_operator::i(1) + + cudaq::spin_operator::x(2); + + auto product = original_product * sum; + auto reverse = sum * original_product; + + ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + + auto got_matrix = product.to_matrix(); + auto got_matrix_reverse = reverse.to_matrix(); + + std::vector matrices_0_0 = { + utils::id_matrix(2), utils::id_matrix(2), utils::PauliZ_matrix()}; + std::vector matrices_0_1 = { + utils::id_matrix(2), utils::PauliY_matrix(), utils::id_matrix(2)}; + std::vector matrices_1_0 = { + utils::id_matrix(2), utils::id_matrix(2), utils::id_matrix(2)}; + std::vector matrices_1_1 = { + utils::PauliX_matrix(), utils::id_matrix(2), utils::id_matrix(2)}; + auto product_matrix = + cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * + cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); + auto sum_matrix = + cudaq::kronecker(matrices_1_0.begin(), matrices_1_0.end()) + + cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); + + auto want_matrix = product_matrix * sum_matrix; + auto want_matrix_reverse = sum_matrix * product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } } TEST(OperatorExpressions, checkCustomProductOps) { diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp index 41224b7f7f..8de87773bf 100644 --- a/unittests/dynamics/spin_operator.cpp +++ b/unittests/dynamics/spin_operator.cpp @@ -355,3 +355,176 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { utils::checkEqual(want_matrix, got_matrix); } } + +TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { + + /// Keeping this fixed throughout. + std::complex value = 0.125 + 0.5j; + + /// `spin_operator + operator_sum` and `operator_sum + + /// spin_operator` + { + auto self = cudaq::spin_operator::y(2); + auto operator_sum = cudaq::spin_operator::y(2) + + cudaq::spin_operator::x(1); + + auto got = self + operator_sum; + auto reverse = operator_sum + self; + + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto self_full = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto term_0_full = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + + auto got_matrix = got.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + auto want_matrix = self_full + term_0_full + term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full + self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `spin_operator - operator_sum` and `operator_sum - + /// spin_operator` + { + auto self = cudaq::spin_operator::i(0); + auto operator_sum = cudaq::spin_operator::x(0) + + cudaq::spin_operator::z(1); + + auto got = self - operator_sum; + auto reverse = operator_sum - self; + + ASSERT_TRUE(got.n_terms() == 3); + ASSERT_TRUE(reverse.n_terms() == 3); + + auto self_full = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(2)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto term_1_full = cudaq::kronecker(utils::PauliZ_matrix(), + utils::id_matrix(2)); + + auto got_matrix = got.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + auto want_matrix = self_full - term_0_full - term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full - self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `spin_operator * operator_sum` and `operator_sum * + /// spin_operator` + { + auto self = cudaq::spin_operator::y(0); + auto operator_sum = cudaq::spin_operator::x(0) + + cudaq::spin_operator::y(2); + + auto got = self * operator_sum; + auto reverse = operator_sum * self; + + ASSERT_TRUE(got.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); + for (auto &term : got.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + for (auto &term : reverse.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + + auto self_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliY_matrix()); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto term_1_full = cudaq::kronecker(utils::PauliY_matrix(), + utils::id_matrix(2)); + auto sum_full = term_0_full + term_1_full; + + auto got_matrix = got.to_matrix(); + auto got_reverse_matrix = reverse.to_matrix(); + auto want_matrix = self_full * sum_full; + auto want_reverse_matrix = sum_full * self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + /// `operator_sum += spin_operator` + { + auto operator_sum = cudaq::spin_operator::z(0) + + cudaq::spin_operator::x(2); + operator_sum += cudaq::spin_operator::y(0); + + ASSERT_TRUE(operator_sum.n_terms() == 3); + + auto self_full = + cudaq::kronecker(utils::id_matrix(2), + utils::PauliZ_matrix()); + auto term_0_full = cudaq::kronecker(utils::PauliX_matrix(), + utils::id_matrix(2)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliY_matrix()); + + auto got_matrix = operator_sum.to_matrix(); + auto want_matrix = term_0_full + term_1_full + self_full; + utils::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum -= spin_operator` + { + auto operator_sum = cudaq::spin_operator::x(0) + + cudaq::spin_operator::i(1); + operator_sum -= cudaq::spin_operator::x(0); + + ASSERT_TRUE(operator_sum.n_terms() == 3); + + auto self_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + auto term_0_full = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(2)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliX_matrix()); + + auto got_matrix = operator_sum.to_matrix(); + auto want_matrix = term_0_full + term_1_full - self_full; + utils::checkEqual(want_matrix, got_matrix); + } + + /// `operator_sum *= spin_operator` + { + auto self = cudaq::spin_operator::i(0); + auto operator_sum = cudaq::spin_operator::y(0) + + cudaq::spin_operator::z(1); + + operator_sum *= self; + + ASSERT_TRUE(operator_sum.n_terms() == 2); + for (auto &term : operator_sum.get_terms()) + ASSERT_TRUE(term.n_terms() == 2); + + auto self_full = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(2)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(2), + utils::PauliY_matrix()); + auto term_1_full = cudaq::kronecker(utils::PauliZ_matrix(), + utils::id_matrix(2)); + auto sum_full = term_0_full + term_1_full; + + auto got_matrix = operator_sum.to_matrix(); + auto want_matrix = sum_full * self_full; + utils::checkEqual(want_matrix, got_matrix); + } +} + +TEST(OperatorExpressions, checkSpinOpsDegreeVerification) { + auto op1 = cudaq::spin_operator::z(1); + auto op2 = cudaq::spin_operator::x(0); + std::map dimensions = {{0, 1}, {1, 3}}; + + ASSERT_THROW(op1.to_matrix({{1, 3}}), std::runtime_error); + ASSERT_THROW((op1 * op2).to_matrix({{0, 3}, {1, 3}}), std::runtime_error); + ASSERT_THROW((op1 + op2).to_matrix({{0, 3}}), std::runtime_error); + ASSERT_NO_THROW(op1.to_matrix({{0, 3}})); +} \ No newline at end of file From c277d11c74f1b230b6d3a082857450c1f2e8bc7f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 7 Feb 2025 13:49:15 +0000 Subject: [PATCH 258/311] commit before removing inheritance Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/manipulation.h | 2 + runtime/cudaq/dynamics/matrix_operators.cpp | 47 ++++++++++++++++- runtime/cudaq/dynamics/matrix_operators.h | 11 +++- runtime/cudaq/dynamics/operator_sum.cpp | 54 +++++++++++++++----- runtime/cudaq/dynamics/product_operators.cpp | 45 +++++++++++----- runtime/cudaq/dynamics/spin_operators.cpp | 10 ++++ runtime/cudaq/dynamics/spin_operators.h | 6 ++- runtime/cudaq/dynamics/templates.h | 1 - runtime/cudaq/operators.h | 39 ++++++++------ unittests/dynamics/product_operator.cpp | 32 +++++++++++- 10 files changed, 200 insertions(+), 47 deletions(-) diff --git a/runtime/cudaq/dynamics/manipulation.h b/runtime/cudaq/dynamics/manipulation.h index 95362cf462..1ce127da8b 100644 --- a/runtime/cudaq/dynamics/manipulation.h +++ b/runtime/cudaq/dynamics/manipulation.h @@ -6,6 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include #include #include "cudaq/utils/tensor.h" diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index e742ca421b..960123e356 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -8,6 +8,7 @@ #include "cudaq/operators.h" #include "matrix_operators.h" +#include "spin_operators.h" #include #include @@ -41,10 +42,34 @@ bool matrix_operator::is_identity() const { // constructors matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), targets(degrees) {} + : id(operator_id), targets(degrees) { + assert(this->targets.size() > 0); + } matrix_operator::matrix_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), targets(std::move(degrees)) {} + : id(operator_id), targets(std::move(degrees)) { + assert(this->targets.size() > 0); + } + +template, bool>> +matrix_operator::matrix_operator(const T &other) { + this->targets = other.degrees(); + this->id = typeid(other).name() + std::to_string(this->targets.size()) + other.to_string(false); + if (matrix_operator::m_ops.find(this->id) == matrix_operator::m_ops.end()) { + auto func = [targets = other.degrees(), other](std::vector dimensions, + std::map> _none) { + std::map dims; + for(auto i = 0; i < dimensions.size(); ++i) + dims[targets[i]] = dimensions[i]; + return other.to_matrix(dims, std::move(_none)); + }; + // the to_matrix method on the spin op will check the dimensions, so we allow arbitrary here + std::vector required_dimensions (this->targets.size(), -1); + matrix_operator::define(this->id, std::move(required_dimensions), func); + } +} + +template matrix_operator::matrix_operator(const spin_operator &other); matrix_operator::matrix_operator(const matrix_operator &other) : targets(other.targets), id(other.id) {} @@ -62,6 +87,14 @@ matrix_operator& matrix_operator::operator=(const matrix_operator& other) { return *this; } +template::value && std::is_base_of_v, bool>> +matrix_operator& matrix_operator::operator=(const T& other) { + *this = matrix_operator(other); + return *this; +} + +template matrix_operator& matrix_operator::operator=(const spin_operator& other); + matrix_operator& matrix_operator::operator=(matrix_operator &&other) { if (this != &other) { this->targets = std::move(other.targets); @@ -102,6 +135,16 @@ matrix_2 matrix_operator::to_matrix( return it->second.generate_matrix(relevant_dimensions, parameters); } +std::string matrix_operator::to_string(bool include_degrees) const { + if (!include_degrees) return this->id; + else if (this->targets.size() == 0) return this->id + "()"; + auto it = this->targets.begin(); + std::string str = this->id + "(" + std::to_string(*it++); + while (it != this->targets.end()) + str += ", " + std::to_string(*it++); + return str + ")"; +} + // comparisons bool matrix_operator::operator==(const matrix_operator &other) const { diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index bef53355fd..e7ba0d3ed6 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -6,6 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include #include #include "cudaq/operators.h" @@ -72,9 +74,11 @@ class matrix_operator : operator_handler{ /// @arg degrees : the degrees of freedom that the operator acts upon. matrix_operator(std::string operator_id, const std::vector °rees); - // constructor matrix_operator(std::string operator_id, std::vector &°rees); + template, bool> = true> + matrix_operator(const T &other); + // copy constructor matrix_operator(const matrix_operator &other); @@ -85,6 +89,9 @@ class matrix_operator : operator_handler{ // assignments + template::value && std::is_base_of_v, bool> = true> + matrix_operator& operator=(const T& other); + // assignment operator matrix_operator& operator=(const matrix_operator& other); @@ -101,6 +108,8 @@ class matrix_operator : operator_handler{ virtual matrix_2 to_matrix(std::map &dimensions, std::map> parameters = {}) const; + virtual std::string to_string(bool include_degrees) const; + // comparisons /// @brief True, if the other value is an elementary operator with the same id diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index d5392948cd..02f8642df4 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -140,12 +140,12 @@ INSTANTIATE_SUM_PROPERTIES(spin_operator); // constructors -template -template -operator_sum::operator_sum(const Args &...args) { - this->terms.reserve(sizeof...(Args)); - this->coefficients.reserve(sizeof...(Args)); - aggregate_terms(args...); +template +template, Args>...>::value, bool>> +operator_sum::operator_sum(const Args&... args) { + this->terms.reserve(sizeof...(Args)); + this->coefficients.reserve(sizeof...(Args)); + aggregate_terms(args...); } template @@ -169,7 +169,21 @@ operator_sum::operator_sum( } } -template +template +template::value && std::is_constructible::value, bool>> +operator_sum::operator_sum(const operator_sum &other) { + this->coefficients = other.coefficients; + this->terms.reserve(other.terms.size()); + for (const auto &term : other.terms) { + std::vector other_terms; + other_terms.reserve(other.terms.size()); + for (const T &op : term) + other_terms.push_back(op); + this->terms.push_back(std::move(other_terms)); + } +} + +template operator_sum::operator_sum(const operator_sum &other) : coefficients(other.coefficients), terms(other.terms) {} @@ -203,21 +217,30 @@ operator_sum::operator_sum(operator_sum &&other) template \ operator_sum::operator_sum(operator_sum &&other); +template +operator_sum::operator_sum(const operator_sum &other); + INSTANTIATE_SUM_CONSTRUCTORS(matrix_operator); INSTANTIATE_SUM_CONSTRUCTORS(spin_operator); // assignments -template -operator_sum & -operator_sum::operator=(const operator_sum &other) { - if (this != &other) { - coefficients = other.coefficients; - terms = other.terms; - } +template +template::value && std::is_constructible::value, bool>> +operator_sum& operator_sum::operator=(const operator_sum &other) { + *this = operator_sum(other); return *this; } +template +operator_sum& operator_sum::operator=(const operator_sum &other) { + if (this != &other) { + coefficients = other.coefficients; + terms = other.terms; + } + return *this; +} + template operator_sum & operator_sum::operator=(operator_sum &&other) { @@ -238,6 +261,9 @@ operator_sum::operator=(operator_sum &&other) { operator_sum& operator_sum::operator=( \ operator_sum &&other); +template +operator_sum& operator_sum::operator=(const operator_sum &other); + INSTANTIATE_SUM_ASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_ASSIGNMENTS(spin_operator); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index d575638ebb..1e06621cda 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -26,7 +26,7 @@ template void product_operator::aggregate_terms() {} template -template +template void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { this->terms[0].push_back(head); aggregate_terms(std::forward(args)...); @@ -88,7 +88,7 @@ EvaluatedMatrix product_operator::m_evaluate( \ template \ void product_operator::aggregate_terms(const HandlerTy &item1, \ - const HandlerTy &item2); \ + const HandlerTy &item2); \ \ template \ void product_operator::aggregate_terms(const HandlerTy &item1, \ @@ -150,10 +150,9 @@ INSTANTIATE_PRODUCT_PROPERTIES(spin_operator); // constructors -template -template -product_operator::product_operator(scalar_operator coefficient, - const Args &...args) { +template +template...>::value, bool>> +product_operator::product_operator(scalar_operator coefficient, const Args&... args) { this->coefficients.push_back(std::move(coefficient)); std::vector ops = {}; ops.reserve(sizeof...(Args)); @@ -176,9 +175,19 @@ product_operator::product_operator( this->coefficients.push_back(std::move(coefficient)); } -template -product_operator::product_operator( - const product_operator &other) { +template +template::value && std::is_constructible::value, bool>> +product_operator::product_operator(const product_operator &other) { + this->coefficients = other.coefficients; + std::vector other_terms; + other_terms.reserve(other.terms.size()); + for (const T &term : other.terms[0]) + other_terms.push_back(term); + this->terms.push_back(std::move(other_terms)); +} + +template +product_operator::product_operator(const product_operator &other) { this->terms = other.terms; this->coefficients = other.coefficients; } @@ -226,14 +235,23 @@ product_operator::product_operator( product_operator::product_operator( \ product_operator &&other); +template +product_operator::product_operator(const product_operator &other); + INSTANTIATE_PRODUCT_CONSTRUCTORS(matrix_operator); INSTANTIATE_PRODUCT_CONSTRUCTORS(spin_operator); // assignments -template -product_operator &product_operator::operator=( - const product_operator &other) { +template +template::value && std::is_constructible::value, bool>> +product_operator& product_operator::operator=(const product_operator &other) { + *this = product_operator(other); + return *this; +} + +template +product_operator& product_operator::operator=(const product_operator &other) { if (this != &other) { this->terms = other.terms; this->coefficients = other.coefficients; @@ -261,6 +279,9 @@ product_operator::operator=(product_operator &&other) { product_operator& product_operator::operator=( \ product_operator &&other); +template +product_operator& product_operator::operator=(const product_operator &other); + INSTANTIATE_PRODUCT_ASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_ASSIGNMENTS(spin_operator); diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 95f070af63..2f625bc69d 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -57,6 +57,16 @@ matrix_2 spin_operator::to_matrix(std::map &dimensions, return mat; } +std::string spin_operator::to_string(bool include_degrees) const { + std::string op_str; + if (this->id == 1) op_str = "Z"; + else if (this->id == 2) op_str = "X"; + else if (this->id == 3) op_str = "Y"; + else op_str = "I"; + if (include_degrees) return op_str + "(" + std::to_string(target) + ")"; + else return op_str; +} + // comparisons bool spin_operator::operator==(const spin_operator &other) const { diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 1e3cf46ed7..dfab3d18ae 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -6,6 +6,8 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include #include #include "cudaq/operators.h" @@ -24,7 +26,7 @@ class spin_operator : operator_handler{ int id; int target; - spin_operator(int id, int target); + spin_operator(int op, int target); public: @@ -52,6 +54,8 @@ class spin_operator : operator_handler{ virtual matrix_2 to_matrix(std::map &dimensions, std::map> parameters = {}) const; + virtual std::string to_string(bool include_degrees) const; + // comparisons bool operator==(const spin_operator &other) const; diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 5e3ccdd85e..7fea2dd812 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -// #include #include #include diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 90b135cd04..8bcbe68f48 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -1,4 +1,4 @@ -/******************************************************************************* +/****************************************************************-*- C++ -*-**** * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * @@ -147,8 +147,9 @@ class scalar_operator { /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods /// to convert them to data types that can. -template // handler needs to inherit from operation_handler +template class operator_sum { +template friend class operator_sum; private: @@ -178,17 +179,16 @@ class operator_sum { // constructors and destructors - template , Args>...>::value, - void>> - operator_sum(const Args &...args); + template, Args>...>::value, bool> = true> + operator_sum(const Args&... args); operator_sum(const std::vector> &terms); operator_sum(std::vector> &&terms); + template::value && std::is_constructible::value, bool> = true> + operator_sum(const operator_sum &other); + // copy constructor operator_sum(const operator_sum &other); @@ -199,6 +199,9 @@ class operator_sum { // assignments + template::value && std::is_constructible::value, bool> = true> + operator_sum& operator=(const operator_sum &other); + // assignment operator operator_sum &operator=(const operator_sum &other); @@ -332,14 +335,15 @@ class operator_sum { /// elementary and scalar operators. Operator expressions cannot be used within /// quantum kernels, but they provide methods to convert them to data types /// that can. -template // handler needs to inherit from operation_handler +template class product_operator : public operator_sum { +template friend class product_operator; private: void aggregate_terms(); template - void aggregate_terms(const HandlerTy &head, Args &&...args); + void aggregate_terms(const HandlerTy &head, Args&& ... args); EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; @@ -363,11 +367,8 @@ class product_operator : public operator_sum { // constructors and destructors - template < - class... Args, - class = std::enable_if_t< - std::conjunction...>::value, void>> - product_operator(scalar_operator coefficient, const Args &...args); + template...>::value, bool> = true> + product_operator(scalar_operator coefficient, const Args&... args); product_operator(scalar_operator coefficient, const std::vector &atomic_operators); @@ -375,6 +376,9 @@ class product_operator : public operator_sum { product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + template::value && std::is_constructible::value, bool> = true> + product_operator(const product_operator &other); + // copy constructor product_operator(const product_operator &other); @@ -385,6 +389,9 @@ class product_operator : public operator_sum { // assignments + template::value && std::is_constructible::value, bool> = true> + product_operator& operator=(const product_operator &other); + // assignment operator product_operator & operator=(const product_operator &other); @@ -516,6 +523,8 @@ class operator_handler { /// degrees of freedom: `{0 : 2, 1 : 2}`. virtual matrix_2 to_matrix(std::map &dimensions, std::map> parameters = {}) const = 0; + + virtual std::string to_string(bool include_degrees = true) const = 0; }; /// @brief Representation of a time-dependent Hamiltonian for Rydberg system diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index 851133af5a..faa4ae9858 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -789,7 +789,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { // FIXME: add tests to combine general products with spin ops TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { - int level_count = 3; + int level_count = 2; // `product_operator + matrix_operator` { @@ -822,6 +822,36 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } + // `product_operator + spin_operator` + { + auto product = cudaq::matrix_operator::annihilate(0) * + cudaq::matrix_operator::annihilate(1); + auto elementary = cudaq::spin_operator::x(1); + + auto sum = product + elementary; + //auto reverse = elementary + product; + + ASSERT_TRUE(sum.n_terms() == 2); + //ASSERT_TRUE(reverse.n_terms() == 2); + + auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); + //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + + auto product_matrix = + cudaq::kronecker(utils::id_matrix(level_count), + utils::annihilate_matrix(level_count)) * + cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::id_matrix(level_count)); + auto elementary_matrix = cudaq::kronecker( + utils::PauliX_matrix(), utils::id_matrix(level_count)); + + auto want_matrix = product_matrix + elementary_matrix; + auto want_matrix_reverse = elementary_matrix + product_matrix; + + utils::checkEqual(want_matrix, got_matrix); + //utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + // `product_operator - matrix_operator` { auto product = cudaq::matrix_operator::annihilate(0) * From aa86a87840379e500c741dd467272d624c4c81cf Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 7 Feb 2025 13:50:11 +0000 Subject: [PATCH 259/311] removing the inheritance from operator sum Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 138 ++++++------ runtime/cudaq/dynamics/product_operators.cpp | 220 +++++++++---------- runtime/cudaq/operators.h | 14 +- unittests/dynamics/utils.cpp | 5 +- 4 files changed, 184 insertions(+), 193 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 02f8642df4..29d55e7f9d 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -63,13 +63,12 @@ EvaluatedMatrix operator_sum::m_evaluate( template void operator_sum::aggregate_terms() {} -template -template -void operator_sum::aggregate_terms( - const product_operator &head, Args &&...args) { - this->terms.push_back(head.terms[0]); - this->coefficients.push_back(head.coefficients[0]); - aggregate_terms(std::forward(args)...); +template +template +void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { + this->terms.push_back(head.operators); + this->coefficients.push_back(head.coefficient); + aggregate_terms(std::forward(args)...); } #define INSTANTIATE_SUM_PRIVATE_METHODS(HandlerTy) \ @@ -78,7 +77,8 @@ void operator_sum::aggregate_terms( EvaluatedMatrix operator_sum::m_evaluate( \ MatrixArithmetics arithmetics, bool pad_terms) const; \ \ - /* no overload for a single product, since we don't want a constructor for a single term */ \ + template \ + void operator_sum::aggregate_terms(const product_operator &item2); \ \ template \ void operator_sum::aggregate_terms(const product_operator &item1, \ @@ -148,25 +148,23 @@ operator_sum::operator_sum(const Args&... args) { aggregate_terms(args...); } -template -operator_sum::operator_sum( - const std::vector> &terms) { - this->terms.reserve(terms.size()); - this->coefficients.reserve(terms.size()); - for (const product_operator &term : terms) { - this->terms.push_back(term.terms[0]); - this->coefficients.push_back(term.coefficients[0]); - } +template +operator_sum::operator_sum(const std::vector> &terms) { + this->terms.reserve(terms.size()); + this->coefficients.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(term.operators); + this->coefficients.push_back(term.coefficient); + } } -template -operator_sum::operator_sum( - std::vector> &&terms) { - this->terms.reserve(terms.size()); - for (const product_operator &term : terms) { - this->terms.push_back(std::move(term.terms[0])); - this->coefficients.push_back(std::move(term.coefficients[0])); - } +template +operator_sum::operator_sum(std::vector> &&terms) { + this->terms.reserve(terms.size()); + for (const product_operator& term : terms) { + this->terms.push_back(std::move(term.operators)); + this->coefficients.push_back(std::move(term.coefficient)); + } } template @@ -194,7 +192,8 @@ operator_sum::operator_sum(operator_sum &&other) #define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ \ - /* no constructor for a single product, since that one should remain a product op */ \ + template \ + operator_sum::operator_sum(const product_operator &item2); \ \ template \ operator_sum::operator_sum(const product_operator &item1, \ @@ -466,15 +465,15 @@ operator_sum operator_sum::operator*( std::vector coefficients; coefficients.reserve(this->coefficients.size()); for (auto &coeff : this->coefficients) - coefficients.push_back(other.coefficients[0] * coeff); + coefficients.push_back(other.coefficient * coeff); std::vector> terms; terms.reserve(this->terms.size()); for (auto &term : this->terms) { std::vector prod; - prod.reserve(term.size() + other.terms[0].size()); - for (auto &op : term) + prod.reserve(term.size() + other.operators.size()); + for (auto &op : term) prod.push_back(op); - for (auto &op : other.terms[0]) + for (auto &op : other.operators) prod.push_back(op); terms.push_back(std::move(prod)); } @@ -484,24 +483,24 @@ operator_sum operator_sum::operator*( return sum; } -#define SUM_ADDITION_PRODUCT(op) \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - coefficients.push_back(op other.coefficients[0]); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - terms.push_back(other.terms[0]); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_PRODUCT(op) \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const { \ + std::vector coefficients; \ + coefficients.reserve(this->coefficients.size() + 1); \ + for (auto &coeff : this->coefficients) \ + coefficients.push_back(coeff); \ + coefficients.push_back(op other.coefficient); \ + std::vector> terms; \ + terms.reserve(this->terms.size() + 1); \ + for (auto &term : this->terms) \ + terms.push_back(term); \ + terms.push_back(other.operators); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } SUM_ADDITION_PRODUCT(+) @@ -641,22 +640,22 @@ template operator_sum & operator_sum::operator*=(const product_operator &other) { for (auto &coeff : this->coefficients) - coeff *= other.coefficients[0]; + coeff *= other.coefficient; for (auto &term : this->terms) { - term.reserve(term.size() + other.terms[0].size()); - for (auto &op : other.terms[0]) + term.reserve(term.size() + other.operators.size()); + for (auto &op : other.operators) term.push_back(op); } return *this; } -#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - const product_operator &other) { \ - this->coefficients.push_back(op other.coefficients[0]); \ - this->terms.push_back(other.terms[0]); \ - return *this; \ +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const product_operator &other) { \ + this->coefficients.push_back(op other.coefficient); \ + this->terms.push_back(other.operators); \ + return *this; \ } SUM_ADDITION_PRODUCT_ASSIGNMENT(+) @@ -671,18 +670,17 @@ operator_sum::operator*=(const operator_sum &other) { return *this; } -#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - const operator_sum &other) { \ - this->coefficients.reserve(this->coefficients.size() + \ - other.coefficients.size()); \ - for (auto &coeff : other.coefficients) \ - this->coefficients.push_back(op coeff); \ - this->terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &term : other.terms) \ - this->terms.push_back(term); \ - return *this; \ +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const operator_sum &other) { \ + this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + for (auto &coeff : other.coefficients) \ + this->coefficients.push_back(op coeff); \ + this->terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &term : other.terms) \ + this->terms.push_back(term); \ + return *this; \ } SUM_ADDITION_SUM_ASSIGNMENT(+); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 1e06621cda..6dec485a10 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -28,7 +28,7 @@ void product_operator::aggregate_terms() {} template template void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { - this->terms[0].push_back(head); + this->operators.push_back(head); aggregate_terms(std::forward(args)...); } @@ -37,7 +37,6 @@ void product_operator::aggregate_terms(const HandlerTy &head, Args&& template EvaluatedMatrix product_operator::m_evaluate( MatrixArithmetics arithmetics, bool pad_terms) const { - const std::vector &terms = this->terms[0]; auto degrees = this->degrees(); cudaq::matrix_2 result; @@ -60,21 +59,21 @@ EvaluatedMatrix product_operator::m_evaluate( return arithmetics.tensor(std::move(ids), EvaluatedMatrix(op_degrees, op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters))); }; - auto coefficient = this->coefficients[0].evaluate(arithmetics.m_parameters); - if (terms.size() > 0) { + auto coefficient = this->coefficient.evaluate(arithmetics.m_parameters); + if (this->operators.size() > 0) { if (pad_terms) { - EvaluatedMatrix prod = padded_op(terms[0]); - for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto op_degrees = terms[op_idx].degrees(); - if (op_degrees.size() != 1 || !terms[op_idx].is_identity()) - prod = arithmetics.mul(std::move(prod), padded_op(terms[op_idx])); + EvaluatedMatrix prod = padded_op(this->operators[0]); + for (auto op_idx = 1; op_idx < this->operators.size(); ++op_idx) { + auto op_degrees = this->operators[op_idx].degrees(); + if (op_degrees.size() != 1 || !this->operators[op_idx].is_identity()) + prod = arithmetics.mul(std::move(prod), padded_op(this->operators[op_idx])); } return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); } else { - EvaluatedMatrix prod(terms[0].degrees(), terms[0].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); - for (auto op_idx = 1; op_idx < terms.size(); ++op_idx) { - auto mat = terms[op_idx].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); - prod = arithmetics.mul(std::move(prod), EvaluatedMatrix(terms[op_idx].degrees(), mat)); + EvaluatedMatrix prod(this->operators[0].degrees(), this->operators[0].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); + for (auto op_idx = 1; op_idx < this->operators.size(); ++op_idx) { + auto mat = this->operators[op_idx].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); + prod = arithmetics.mul(std::move(prod), EvaluatedMatrix(this->operators[op_idx].degrees(), mat)); } return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); } @@ -107,7 +106,7 @@ INSTANTIATE_PRODUCT_PRIVATE_METHODS(spin_operator); template std::vector product_operator::degrees() const { std::set unsorted_degrees; - for (const HandlerTy &term : this->terms[0]) { + for (const HandlerTy &term : this->operators) { auto term_degrees = term.degrees(); unsorted_degrees.insert(term_degrees.begin(), term_degrees.end()); } @@ -116,19 +115,19 @@ std::vector product_operator::degrees() const { return cudaq::detail::canonicalize_degrees(degrees); } -template -int product_operator::n_terms() const { - return this->terms[0].size(); +template +int product_operator::n_terms() const { + return this->operators.size(); } -template -std::vector product_operator::get_terms() const { - return this->terms[0]; +template +std::vector product_operator::get_terms() const { + return this->operators; } -template -scalar_operator product_operator::get_coefficient() const { - return this->coefficients[0]; +template +scalar_operator product_operator::get_coefficient() const { + return this->coefficient; } #define INSTANTIATE_PRODUCT_PROPERTIES(HandlerTy) \ @@ -152,51 +151,42 @@ INSTANTIATE_PRODUCT_PROPERTIES(spin_operator); template template...>::value, bool>> -product_operator::product_operator(scalar_operator coefficient, const Args&... args) { - this->coefficients.push_back(std::move(coefficient)); - std::vector ops = {}; - ops.reserve(sizeof...(Args)); - this->terms.push_back(ops); +product_operator::product_operator(scalar_operator coefficient, const Args&... args) + : coefficient(std::move(coefficient)) { + this->operators.reserve(sizeof...(Args)); aggregate_terms(args...); } -template -product_operator::product_operator( - scalar_operator coefficient, - const std::vector &atomic_operators) { - this->terms.push_back(atomic_operators); - this->coefficients.push_back(std::move(coefficient)); +template +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) + : coefficient(std::move(coefficient)){ + this->operators = atomic_operators; } -template -product_operator::product_operator( - scalar_operator coefficient, std::vector &&atomic_operators) { - this->terms.push_back(std::move(atomic_operators)); - this->coefficients.push_back(std::move(coefficient)); +template +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) + : coefficient(std::move(coefficient)) { + this->operators = std::move(atomic_operators); } template template::value && std::is_constructible::value, bool>> -product_operator::product_operator(const product_operator &other) { - this->coefficients = other.coefficients; - std::vector other_terms; - other_terms.reserve(other.terms.size()); - for (const T &term : other.terms[0]) - other_terms.push_back(term); - this->terms.push_back(std::move(other_terms)); +product_operator::product_operator(const product_operator &other) + : coefficient(other.coefficient) { + for (const T &op : other.operators) + this->operators.push_back(op); } template -product_operator::product_operator(const product_operator &other) { - this->terms = other.terms; - this->coefficients = other.coefficients; +product_operator::product_operator(const product_operator &other) + : coefficient(other.coefficient) { + this->operators = other.operators; } -template -product_operator::product_operator( - product_operator &&other) { - this->terms = std::move(other.terms); - this->coefficients = std::move(other.coefficients); +template +product_operator::product_operator(product_operator &&other) + : coefficient(std::move(other.coefficient)) { + this->operators = std::move(other.operators); } #define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ @@ -253,8 +243,8 @@ product_operator& product_operator::operator=(const produc template product_operator& product_operator::operator=(const product_operator &other) { if (this != &other) { - this->terms = other.terms; - this->coefficients = other.coefficients; + this->coefficient = other.coefficient; + this->operators = other.operators; } return *this; } @@ -263,8 +253,8 @@ template product_operator & product_operator::operator=(product_operator &&other) { if (this != &other) { - this->coefficients = std::move(other.coefficients); - this->terms = std::move(other.terms); + this->coefficient = std::move(other.coefficient); + this->operators = std::move(other.operators); } return *this; } @@ -332,8 +322,7 @@ INSTANTIATE_PRODUCT_COMPARISONS(spin_operator); template product_operator product_operator::operator-() const { - return product_operator(-1. * this->coefficients[0], - this->terms[0]); + return product_operator(-1. * this->coefficient, this->operators); } template @@ -354,12 +343,11 @@ INSTANTIATE_PRODUCT_UNARY_OPS(spin_operator); // right-hand arithmetics -#define PRODUCT_MULTIPLICATION(otherTy) \ - template \ - product_operator product_operator::operator*( \ - otherTy other) const { \ - return product_operator(other * this->coefficients[0], \ - this->terms[0]); \ +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficient, this->operators); \ } PRODUCT_MULTIPLICATION(double); @@ -385,11 +373,11 @@ template product_operator product_operator::operator*(const HandlerTy &other) const { std::vector terms; - terms.reserve(this->terms[0].size() + 1); - for (auto &term : this->terms[0]) + terms.reserve(this->operators.size() + 1); + for (auto &term : this->operators) terms.push_back(term); terms.push_back(other); - return product_operator(this->coefficients[0], std::move(terms)); + return product_operator(this->coefficient, std::move(terms)); } #define PRODUCT_ADDITION_HANDLER(op) \ @@ -437,13 +425,12 @@ template product_operator product_operator::operator*( const product_operator &other) const { std::vector terms; - terms.reserve(this->terms[0].size() + other.terms[0].size()); - for (auto &term : this->terms[0]) + terms.reserve(this->operators.size() + other.operators.size()); + for (auto &term : this->operators) terms.push_back(term); - for (auto &term : other.terms[0]) + for (auto &term : other.operators) terms.push_back(term); - return product_operator(this->coefficients[0] * other.coefficients[0], - std::move(terms)); + return product_operator(this->coefficient * other.coefficient, std::move(terms)); } #define PRODUCT_ADDITION_PRODUCT(op) \ @@ -462,13 +449,13 @@ operator_sum product_operator::operator*( std::vector coefficients; coefficients.reserve(other.coefficients.size()); for (auto &coeff : other.coefficients) - coefficients.push_back(this->coefficients[0] * coeff); + coefficients.push_back(this->coefficient * coeff); std::vector> terms; terms.reserve(other.terms.size()); for (auto &term : other.terms) { std::vector prod; - prod.reserve(this->terms[0].size() + term.size()); - for (auto &op : this->terms[0]) + prod.reserve(this->operators.size() + term.size()); + for (auto &op : this->operators) prod.push_back(op); for (auto &op : term) prod.push_back(op); @@ -480,24 +467,24 @@ operator_sum product_operator::operator*( return sum; } -#define PRODUCT_ADDITION_SUM(op) \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(other.coefficients.size() + 1); \ - coefficients.push_back(this->coefficients[0]); \ - for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(other.terms.size() + 1); \ - terms.push_back(this->terms[0]); \ - for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + std::vector coefficients; \ + coefficients.reserve(other.coefficients.size() + 1); \ + coefficients.push_back(this->coefficient); \ + for (auto &coeff : other.coefficients) \ + coefficients.push_back(op coeff); \ + std::vector> terms; \ + terms.reserve(other.terms.size() + 1); \ + terms.push_back(this->operators); \ + for (auto &term : other.terms) \ + terms.push_back(term); \ + operator_sum sum; \ + sum.coefficients = std::move(coefficients); \ + sum.terms = std::move(terms); \ + return sum; \ } PRODUCT_ADDITION_SUM(+) @@ -527,12 +514,11 @@ PRODUCT_ADDITION_SUM(-) INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(spin_operator); -#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - product_operator &product_operator::operator*=( \ - otherTy other) { \ - this->coefficients[0] *= other; \ - return *this; \ +#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + product_operator& product_operator::operator*=(otherTy other) { \ + this->coefficient *= other; \ + return *this; \ } PRODUCT_MULTIPLICATION_ASSIGNMENT(double); @@ -540,19 +526,16 @@ PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); template -product_operator & -product_operator::operator*=(const HandlerTy &other) { - this->terms[0].push_back(other); +product_operator& product_operator::operator*=(const HandlerTy &other) { + this->operators.push_back(other); return *this; } template -product_operator &product_operator::operator*=( - const product_operator &other) { - this->coefficients[0] *= other.coefficients[0]; - this->terms[0].reserve(this->terms[0].size() + other.terms[0].size()); - this->terms[0].insert(this->terms[0].end(), other.terms[0].begin(), - other.terms[0].end()); +product_operator& product_operator::operator*=(const product_operator &other) { + this->coefficient *= other.coefficient; + this->operators.reserve(this->operators.size() + other.operators.size()); + this->operators.insert(this->operators.end(), other.operators.begin(), other.operators.end()); return *this; } @@ -574,12 +557,11 @@ INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); // left-hand arithmetics -#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ - template \ - product_operator operator*( \ - otherTy other, const product_operator &self) { \ - return product_operator(other * self.coefficients[0], \ - self.terms[0]); \ +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*(otherTy other, \ + const product_operator &self) { \ + return product_operator(other * self.coefficient, self.operators); \ } PRODUCT_MULTIPLICATION_REVERSE(double); @@ -605,11 +587,11 @@ template product_operator operator*(const HandlerTy &other, const product_operator &self) { std::vector terms; - terms.reserve(self.terms[0].size() + 1); + terms.reserve(self.operators.size() + 1); terms.push_back(other); - for (auto &term : self.terms[0]) + for (auto &term : self.operators) terms.push_back(term); - return product_operator(self.coefficients[0], std::move(terms)); + return product_operator(self.coefficient, std::move(terms)); } #define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 8bcbe68f48..a018c8594b 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -336,8 +336,9 @@ template friend class operator_sum; /// quantum kernels, but they provide methods to convert them to data types /// that can. template -class product_operator : public operator_sum { +class product_operator { template friend class product_operator; +template friend class operator_sum; private: void aggregate_terms(); @@ -350,6 +351,17 @@ template friend class product_operator; template friend EvaluatedMatrix operator_sum::m_evaluate(MatrixArithmetics arithmetics, bool pad_terms) const; +protected: + + std::vector operators; + scalar_operator coefficient; + + template + friend operator_sum::operator_sum(const std::vector> &terms); + + template + friend operator_sum::operator_sum(std::vector> &&terms); + public: // read-only properties diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp index ebf7fbf121..3cdbecc684 100644 --- a/unittests/dynamics/utils.cpp +++ b/unittests/dynamics/utils.cpp @@ -24,9 +24,8 @@ void print(cudaq::matrix_2 mat) { void assert_product_equal(const cudaq::product_operator &got, const std::complex &expected_coefficient, const std::vector &expected_terms) { - - auto sumterms_prod = ((const cudaq::operator_sum&)got).get_terms(); - ASSERT_TRUE(sumterms_prod.size() == 1); + cudaq::operator_sum sum = got; + ASSERT_TRUE(sum.get_terms().size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); ASSERT_TRUE(got.get_terms() == expected_terms); } From 8767426b64722fe3fa00795e13961dfbfbaca376 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 7 Feb 2025 19:23:15 +0000 Subject: [PATCH 260/311] some more clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/matrix_operators.cpp | 40 ++++++--- runtime/cudaq/dynamics/matrix_operators.h | 27 +++--- runtime/cudaq/dynamics/operator_sum.cpp | 48 +++++++---- runtime/cudaq/dynamics/product_operators.cpp | 88 +++++++++++--------- runtime/cudaq/dynamics/spin_operators.cpp | 11 +-- runtime/cudaq/dynamics/spin_operators.h | 3 +- runtime/cudaq/operators.h | 73 ++++++++-------- unittests/dynamics/matrix_operator.cpp | 18 ++-- unittests/dynamics/operator_sum.cpp | 8 +- unittests/dynamics/product_operator.cpp | 10 +-- 10 files changed, 189 insertions(+), 137 deletions(-) diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 960123e356..454815a1e4 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -29,6 +29,20 @@ void matrix_operator::define(std::string operator_id, std::vector expected_ } } +product_operator matrix_operator::create(std::string operator_id, const std::vector °rees) { + auto it = matrix_operator::m_ops.find(operator_id); + if (it == matrix_operator::m_ops.end()) + throw std::range_error("not matrix operator with the name '" + operator_id + "' has been defined"); + return product_operator(matrix_operator(operator_id, degrees)); +} + +product_operator matrix_operator::create(std::string operator_id, std::vector &°rees) { + auto it = matrix_operator::m_ops.find(operator_id); + if (it == matrix_operator::m_ops.end()) + throw std::range_error("not matrix operator with the name '" + operator_id + "' has been defined"); + return product_operator(matrix_operator(operator_id, std::move(degrees))); +} + // read-only properties std::vector matrix_operator::degrees() const { @@ -153,7 +167,8 @@ bool matrix_operator::operator==(const matrix_operator &other) const { // predefined operators -product_operator matrix_operator::identity(int degree) { +// multiplicative identity +matrix_operator matrix_operator::one(int degree) { std::string op_id = "identity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { auto func = [](std::vector dimensions, @@ -169,8 +184,11 @@ product_operator matrix_operator::identity(int degree) { }; matrix_operator::define(op_id, {-1}, std::move(func)); } - auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return matrix_operator(op_id, {degree}); +} + +product_operator matrix_operator::identity(int degree) { + return product_operator(std::move(matrix_operator::one(degree))); } product_operator matrix_operator::annihilate(int degree) { @@ -188,7 +206,7 @@ product_operator matrix_operator::annihilate(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::create(int degree) { @@ -206,7 +224,7 @@ product_operator matrix_operator::create(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::position(int degree) { @@ -228,7 +246,7 @@ product_operator matrix_operator::position(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::momentum(int degree) { @@ -250,7 +268,7 @@ product_operator matrix_operator::momentum(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::number(int degree) { @@ -268,7 +286,7 @@ product_operator matrix_operator::number(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::parity(int degree) { @@ -286,7 +304,7 @@ product_operator matrix_operator::parity(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::displace(int degree) { @@ -313,7 +331,7 @@ product_operator matrix_operator::displace(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } product_operator matrix_operator::squeeze(int degree) { @@ -341,7 +359,7 @@ product_operator matrix_operator::squeeze(int degree) { matrix_operator::define(op_id, {-1}, func); } auto op = matrix_operator(op_id, {degree}); - return product_operator(1., op); + return product_operator(std::move(op)); } // tools for custom operators diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index e7ba0d3ed6..95e062769e 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -20,9 +20,14 @@ class matrix_operator : operator_handler{ static std::map m_ops; +protected: + std::vector targets; std::string id; + matrix_operator(std::string operator_id, const std::vector °rees); + matrix_operator(std::string operator_id, std::vector &°rees); + public: // tools for custom operators @@ -56,6 +61,16 @@ class matrix_operator : operator_handler{ static void define(std::string operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create); + /// @brief Instantiates a custom operator. + /// @arg operator_id : The ID of the operator as specified when it was defined. + /// @arg degrees : the degrees of freedom that the operator acts upon. + static product_operator create(std::string operator_id, const std::vector °rees); + + /// @brief Instantiates a custom operator. + /// @arg operator_id : The ID of the operator as specified when it was defined. + /// @arg degrees : the degrees of freedom that the operator acts upon. + static product_operator create(std::string operator_id, std::vector &°rees); + // read-only properties /// @brief The degrees of freedom that the operator acts on in canonical @@ -66,16 +81,6 @@ class matrix_operator : operator_handler{ // constructors and destructors - // The constructor should never be called directly by the user: - // Keeping it internally documented for now, however. - /// @brief Constructor. - /// @arg operator_id : The ID of the operator as specified when it was - /// defined. - /// @arg degrees : the degrees of freedom that the operator acts upon. - matrix_operator(std::string operator_id, const std::vector °rees); - - matrix_operator(std::string operator_id, std::vector &°rees); - template, bool> = true> matrix_operator(const T &other); @@ -118,6 +123,8 @@ class matrix_operator : operator_handler{ // predefined operators + // multiplicative identity + static matrix_operator one(int degree); static product_operator identity(int degree); static product_operator annihilate(int degree); static product_operator create(int degree); diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 29d55e7f9d..71b9f6dfeb 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -32,13 +32,16 @@ EvaluatedMatrix operator_sum::m_evaluate( // We need to make sure all matrices are of the same size to sum them up. auto paddedTerm = [&arithmetics, °rees = std::as_const(degrees)](product_operator &&term) { + std::vector prod; + prod.reserve(degrees.size()); auto term_degrees = term.degrees(); for (auto degree : degrees) { auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); if (it == term_degrees.end()) - term *= HandlerTy::identity(degree); + prod.push_back(HandlerTy::one(degree)); } - return term; + prod.insert(prod.end(), std::make_move_iterator(term.operators.begin()), std::make_move_iterator(term.operators.end())); + return product_operator(std::move(term.coefficient), std::move(prod)); }; if (pad_terms) { @@ -65,9 +68,9 @@ void operator_sum::aggregate_terms() {} template template -void operator_sum::aggregate_terms(const product_operator &head, Args&& ... args) { - this->terms.push_back(head.operators); - this->coefficients.push_back(head.coefficient); +void operator_sum::aggregate_terms(product_operator &&head, Args&& ... args) { + this->terms.push_back(std::move(head.operators)); + this->coefficients.push_back(std::move(head.coefficient)); aggregate_terms(std::forward(args)...); } @@ -78,16 +81,16 @@ void operator_sum::aggregate_terms(const product_operator MatrixArithmetics arithmetics, bool pad_terms) const; \ \ template \ - void operator_sum::aggregate_terms(const product_operator &item2); \ + void operator_sum::aggregate_terms(product_operator &&item2); \ \ template \ - void operator_sum::aggregate_terms(const product_operator &item1, \ - const product_operator &item2); \ + void operator_sum::aggregate_terms(product_operator &&item1, \ + product_operator &&item2); \ \ template \ - void operator_sum::aggregate_terms(const product_operator &item1, \ - const product_operator &item2, \ - const product_operator &item3); + void operator_sum::aggregate_terms(product_operator &&item1, \ + product_operator &&item2, \ + product_operator &&item3); INSTANTIATE_SUM_PRIVATE_METHODS(matrix_operator); INSTANTIATE_SUM_PRIVATE_METHODS(spin_operator); @@ -140,12 +143,18 @@ INSTANTIATE_SUM_PROPERTIES(spin_operator); // constructors +template +operator_sum::operator_sum(const product_operator &prod) { + this->coefficients.push_back(prod.coefficient); + this->terms.push_back(prod.operators); +} + template template, Args>...>::value, bool>> -operator_sum::operator_sum(const Args&... args) { +operator_sum::operator_sum(Args&&... args) { this->terms.reserve(sizeof...(Args)); this->coefficients.reserve(sizeof...(Args)); - aggregate_terms(args...); + aggregate_terms(std::forward&&>(args)...); } template @@ -196,13 +205,16 @@ operator_sum::operator_sum(operator_sum &&other) operator_sum::operator_sum(const product_operator &item2); \ \ template \ - operator_sum::operator_sum(const product_operator &item1, \ - const product_operator &item2); \ + operator_sum::operator_sum(product_operator &&item2); \ + \ + template \ + operator_sum::operator_sum(product_operator &&item1, \ + product_operator &&item2); \ \ template \ - operator_sum::operator_sum(const product_operator &item1, \ - const product_operator &item2, \ - const product_operator &item3); \ + operator_sum::operator_sum(product_operator &&item1, \ + product_operator &&item2, \ + product_operator &&item3); \ \ template \ operator_sum::operator_sum(const std::vector> &terms); \ diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 6dec485a10..44449dbbc7 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -27,7 +27,7 @@ void product_operator::aggregate_terms() {} template template -void product_operator::aggregate_terms(const HandlerTy &head, Args&& ... args) { +void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... args) { this->operators.push_back(head); aggregate_terms(std::forward(args)...); } @@ -46,7 +46,7 @@ EvaluatedMatrix product_operator::m_evaluate( for (const auto °ree : degrees) { if (std::find(op_degrees.begin(), op_degrees.end(), degree) == op_degrees.end()) { // FIXME: instead of relying on an identity to exist, replace pad_terms with a function to invoke. - auto identity = HandlerTy::identity(degree); + auto identity = HandlerTy::one(degree); padded.push_back(EvaluatedMatrix(identity.degrees(), identity.to_matrix(arithmetics.m_dimensions))); } } @@ -86,13 +86,13 @@ EvaluatedMatrix product_operator::m_evaluate( #define INSTANTIATE_PRODUCT_PRIVATE_METHODS(HandlerTy) \ \ template \ - void product_operator::aggregate_terms(const HandlerTy &item1, \ - const HandlerTy &item2); \ + void product_operator::aggregate_terms(HandlerTy &&item1, \ + HandlerTy &&item2); \ \ template \ - void product_operator::aggregate_terms(const HandlerTy &item1, \ - const HandlerTy &item2, \ - const HandlerTy &item3); \ + void product_operator::aggregate_terms(HandlerTy &&item1, \ + HandlerTy &&item2, \ + HandlerTy &&item3); \ \ template \ EvaluatedMatrix product_operator::m_evaluate( \ @@ -121,7 +121,7 @@ int product_operator::n_terms() const { } template -std::vector product_operator::get_terms() const { +const std::vector& product_operator::get_terms() const { return this->operators; } @@ -139,7 +139,7 @@ scalar_operator product_operator::get_coefficient() const { int product_operator::n_terms() const; \ \ template \ - std::vector product_operator::get_terms() const; \ + const std::vector& product_operator::get_terms() const; \ \ template \ scalar_operator product_operator::get_coefficient() const; @@ -149,12 +149,18 @@ INSTANTIATE_PRODUCT_PROPERTIES(spin_operator); // constructors +template +product_operator::product_operator(HandlerTy &&atomic) + : coefficient(1.) { + this->operators.push_back(std::move(atomic)); +} + template template...>::value, bool>> -product_operator::product_operator(scalar_operator coefficient, const Args&... args) +product_operator::product_operator(scalar_operator coefficient, Args&&... args) : coefficient(std::move(coefficient)) { this->operators.reserve(sizeof...(Args)); - aggregate_terms(args...); + aggregate_terms(std::forward(args)...); } template @@ -195,19 +201,22 @@ product_operator::product_operator(product_operator &&othe product_operator::product_operator(scalar_operator coefficient); \ \ template \ + product_operator::product_operator(HandlerTy &&atomic); \ + \ + template \ product_operator::product_operator(scalar_operator coefficient, \ - const HandlerTy &item1); \ + HandlerTy &&atomic1); \ \ template \ product_operator::product_operator(scalar_operator coefficient, \ - const HandlerTy &item1, \ - const HandlerTy &item2); \ + HandlerTy &&atomic1, \ + HandlerTy &&atomic2); \ \ template \ product_operator::product_operator(scalar_operator coefficient, \ - const HandlerTy &item1, \ - const HandlerTy &item2, \ - const HandlerTy &item3); \ + HandlerTy &&atomic1, \ + HandlerTy &&atomic2, \ + HandlerTy &&atomic3); \ \ template \ product_operator::product_operator( \ @@ -354,12 +363,12 @@ PRODUCT_MULTIPLICATION(double); PRODUCT_MULTIPLICATION(std::complex); PRODUCT_MULTIPLICATION(const scalar_operator &); -#define PRODUCT_ADDITION(otherTy, op) \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) const { \ - return operator_sum(product_operator(op other), \ - *this); \ +#define PRODUCT_ADDITION(otherTy, op) \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const { \ + return operator_sum(product_operator(op other), \ + product_operator(*this)); \ } PRODUCT_ADDITION(double, +); @@ -380,12 +389,12 @@ product_operator::operator*(const HandlerTy &other) const { return product_operator(this->coefficient, std::move(terms)); } -#define PRODUCT_ADDITION_HANDLER(op) \ - template \ - operator_sum product_operator::operator op( \ - const HandlerTy &other) const { \ - return operator_sum(product_operator(op 1., other), \ - *this); \ +#define PRODUCT_ADDITION_HANDLER(op) \ + template \ + operator_sum product_operator::operator op( \ + const HandlerTy &other) const { \ + return operator_sum(product_operator(op 1., HandlerTy(other)), \ + product_operator(*this)); \ } PRODUCT_ADDITION_HANDLER(+) @@ -433,11 +442,11 @@ product_operator product_operator::operator*( return product_operator(this->coefficient * other.coefficient, std::move(terms)); } -#define PRODUCT_ADDITION_PRODUCT(op) \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) const { \ - return operator_sum(op other, *this); \ +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(op other, product_operator(*this)); \ } PRODUCT_ADDITION_PRODUCT(+) @@ -594,12 +603,11 @@ product_operator operator*(const HandlerTy &other, return product_operator(self.coefficient, std::move(terms)); } -#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ - template \ - operator_sum operator op( \ - const HandlerTy &other, const product_operator &self) { \ - return operator_sum(product_operator(1., other), \ - op self); \ +#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ + template \ + operator_sum operator op(const HandlerTy &other, \ + const product_operator &self) { \ + return operator_sum(product_operator(1., HandlerTy(other)), op self); \ } PRODUCT_ADDITION_HANDLER_REVERSE(+) diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 2f625bc69d..af29f51d90 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -75,24 +75,25 @@ bool spin_operator::operator==(const spin_operator &other) const { // defined operators -spin_operator spin_operator::identity(int degree) { +// multiplicative identity +spin_operator spin_operator::one(int degree) { return spin_operator(0, degree); } product_operator spin_operator::i(int degree) { - return product_operator(1., spin_operator(0, degree)); + return product_operator(spin_operator(0, degree)); } product_operator spin_operator::z(int degree) { - return product_operator(1., spin_operator(1, degree)); + return product_operator(spin_operator(1, degree)); } product_operator spin_operator::x(int degree) { - return product_operator(1., spin_operator(2, degree)); + return product_operator(spin_operator(2, degree)); } product_operator spin_operator::y(int degree) { - return product_operator(1., spin_operator(3, degree)); + return product_operator(spin_operator(3, degree)); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index dfab3d18ae..4fef23e4c5 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -62,7 +62,8 @@ class spin_operator : operator_handler{ // defined operators - static spin_operator identity(int degree); + // multiplicative identity + static spin_operator one(int degree); static product_operator i(int degree); static product_operator z(int degree); static product_operator x(int degree); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index a018c8594b..dbaaffd856 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -150,6 +150,7 @@ class scalar_operator { template class operator_sum { template friend class operator_sum; +template friend class product_operator; private: @@ -157,14 +158,21 @@ template friend class operator_sum; void aggregate_terms(); - template - void aggregate_terms(const product_operator &head, Args &&...args); + template + void aggregate_terms(product_operator &&head, Args&& ... args); protected: - operator_sum() = default; + std::vector> terms; std::vector coefficients; + template, Args>...>::value, bool> = true> + operator_sum(Args&&... args); + + operator_sum(const std::vector> &terms); + + operator_sum(std::vector> &&terms); + public: // read-only properties @@ -175,16 +183,12 @@ template friend class operator_sum; /// @brief Return the number of operator terms that make up this operator sum. int n_terms() const; + /// FIXME: GET RID OF THIS (MAKE ITERABLE INSTEAD) std::vector> get_terms() const; // constructors and destructors - template, Args>...>::value, bool> = true> - operator_sum(const Args&... args); - - operator_sum(const std::vector> &terms); - - operator_sum(std::vector> &&terms); + operator_sum(const product_operator& prod); template::value && std::is_constructible::value, bool> = true> operator_sum(const operator_sum &other); @@ -320,15 +324,22 @@ template friend class operator_sum; template friend operator_sum operator-(const T &other, const operator_sum &self); - template - friend operator_sum - product_operator::operator*(const operator_sum &other) const; - template - friend operator_sum - product_operator::operator+(const operator_sum &other) const; - template - friend operator_sum - product_operator::operator-(const operator_sum &other) const; + template + friend operator_sum operator+(double other, const product_operator &self); + template + friend operator_sum operator-(double other, const product_operator &self); + template + friend operator_sum operator+(std::complex other, const product_operator &self); + template + friend operator_sum operator-(std::complex other, const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator+(const T &other, const product_operator &self); + template + friend operator_sum operator-(const T &other, const product_operator &self); }; /// @brief Represents an operator expression consisting of a product of @@ -344,23 +355,21 @@ template friend class operator_sum; void aggregate_terms(); template - void aggregate_terms(const HandlerTy &head, Args&& ... args); + void aggregate_terms(HandlerTy &&head, Args&& ... args); EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; - template - friend EvaluatedMatrix operator_sum::m_evaluate(MatrixArithmetics arithmetics, bool pad_terms) const; - protected: std::vector operators; scalar_operator coefficient; - template - friend operator_sum::operator_sum(const std::vector> &terms); + template...>::value, bool> = true> + product_operator(scalar_operator coefficient, Args&&... args); - template - friend operator_sum::operator_sum(std::vector> &&terms); + product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + + product_operator(scalar_operator coefficient, std::vector &&atomic_operators); public: // read-only properties @@ -373,20 +382,14 @@ template friend class operator_sum; /// operator. int n_terms() const; - std::vector get_terms() const; + /// FIXME: GET RID OF THIS (MAKE ITERABLE INSTEAD) + const std::vector& get_terms() const; scalar_operator get_coefficient() const; // constructors and destructors - template...>::value, bool> = true> - product_operator(scalar_operator coefficient, const Args&... args); - - product_operator(scalar_operator coefficient, - const std::vector &atomic_operators); - - product_operator(scalar_operator coefficient, - std::vector &&atomic_operators); + product_operator(HandlerTy &&atomic); template::value && std::is_constructible::value, bool> = true> product_operator(const product_operator &other); diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index e8dc5fc917..2e40b96a44 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -141,8 +141,8 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { // op 1: // number level on 3 // create level+2 on 1 - auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); - auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 3})); + auto op0 = cudaq::matrix_operator::create("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::create("custom_op1", {1, 3}); auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), utils::position_matrix(level_count + 2)); @@ -345,8 +345,9 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto product = self * other; auto reverse = other * self; - utils::assert_product_equal(product, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); - utils::assert_product_equal(reverse, const_scale_factor, {cudaq::matrix_operator("momentum", {0})}); + auto momentum = cudaq::matrix_operator::momentum(0).get_terms()[0]; + utils::assert_product_equal(product, const_scale_factor, {momentum}); + utils::assert_product_equal(reverse, const_scale_factor, {momentum}); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -370,8 +371,9 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto product = self * other; auto reverse = other * self; - utils::assert_product_equal(product, other.evaluate(), {cudaq::matrix_operator("create", {0})}); - utils::assert_product_equal(reverse, other.evaluate(), {cudaq::matrix_operator("create", {0})}); + auto create = cudaq::matrix_operator::create(0).get_terms()[0]; + utils::assert_product_equal(product, other.evaluate(), {create}); + utils::assert_product_equal(reverse, other.evaluate(), {create}); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -680,8 +682,8 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } - auto custom_op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {3, 1})); - auto custom_op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 0})); + auto custom_op0 = cudaq::matrix_operator::create("custom_op0", {3, 1}); + auto custom_op1 = cudaq::matrix_operator::create("custom_op1", {1, 0}); ASSERT_THROW(op1.to_matrix(), std::runtime_error); ASSERT_THROW(op1.to_matrix({{1, 2}}), std::runtime_error); diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index b81af388b1..4f23398953 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -1300,8 +1300,8 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } - auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); - auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); + auto op0 = cudaq::matrix_operator::create("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::create("custom_op1", {1, 2}); auto sum = op0 + op1; auto sum_reverse = op1 + op0; auto difference = op0 - op1; @@ -1327,8 +1327,8 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { utils::checkEqual(difference.to_matrix(dimensions), diff_expected); utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); - op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {2, 3})); - op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {2, 0})); + op0 = cudaq::matrix_operator::create("custom_op0", {2, 3}); + op1 = cudaq::matrix_operator::create("custom_op1", {2, 0}); sum = op0 + op1; sum_reverse = op1 + op0; difference = op0 - op1; diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index faa4ae9858..2f030956b3 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -46,7 +46,7 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { auto op1 = cudaq::matrix_operator::create(5); auto got = op0 * op1; - utils::assert_product_equal(got, 1., {cudaq::matrix_operator("annihilate", {5}), cudaq::matrix_operator("create", {5})}); + utils::assert_product_equal(got, 1., {op0.get_terms()[0], op1.get_terms()[0]}); ASSERT_TRUE(got.degrees() == want_degrees); auto got_matrix = got.to_matrix({{5, level_count}}); @@ -1569,8 +1569,8 @@ TEST(OperatorExpressions, checkCustomProductOps) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } - auto op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {0, 1})); - auto op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {1, 2})); + auto op0 = cudaq::matrix_operator::create("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::create("custom_op1", {1, 2}); auto product = op0 * op1; auto reverse = op1 * op0; @@ -1589,8 +1589,8 @@ TEST(OperatorExpressions, checkCustomProductOps) { utils::checkEqual(product.to_matrix(dimensions), expected); utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); - op0 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op0", {2, 3})); - op1 = cudaq::product_operator(1., cudaq::matrix_operator("custom_op1", {2, 0})); + op0 = cudaq::matrix_operator::create("custom_op0", {2, 3}); + op1 = cudaq::matrix_operator::create("custom_op1", {2, 0}); product = op0 * op1; reverse = op1 * op0; From 3aa6283fd1c8f49a53ca65403246d6282326d00f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 10 Feb 2025 12:47:44 +0000 Subject: [PATCH 261/311] implementing remaining conversions Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/CMakeLists.txt | 1 + runtime/cudaq/dynamics/boson_operators.cpp | 103 +++++++++ runtime/cudaq/dynamics/boson_operators.h | 76 +++++++ runtime/cudaq/dynamics/matrix_operators.cpp | 16 +- runtime/cudaq/dynamics/matrix_operators.h | 18 +- runtime/cudaq/dynamics/operator_leafs.h | 148 +++++++++++++ runtime/cudaq/dynamics/operator_sum.cpp | 108 ++++++++- runtime/cudaq/dynamics/product_operators.cpp | 67 +++++- runtime/cudaq/dynamics/spin_operators.cpp | 7 +- runtime/cudaq/dynamics/spin_operators.h | 17 +- runtime/cudaq/dynamics/templates.h | 221 ++++++++++++------- runtime/cudaq/operators.h | 157 +------------ unittests/dynamics/matrix_operator.cpp | 8 +- unittests/dynamics/operator_sum.cpp | 8 +- unittests/dynamics/product_operator.cpp | 26 ++- 15 files changed, 703 insertions(+), 278 deletions(-) create mode 100644 runtime/cudaq/dynamics/boson_operators.cpp create mode 100644 runtime/cudaq/dynamics/boson_operators.h create mode 100644 runtime/cudaq/dynamics/operator_leafs.h diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index cc4850ab67..cbaa3d544c 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -14,6 +14,7 @@ set(CUDAQ_OPS_SRC callback.cpp scalar_operators.cpp spin_operators.cpp + boson_operators.cpp matrix_operators.cpp product_operators.cpp operator_sum.cpp diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp new file mode 100644 index 0000000000..945b6d0e08 --- /dev/null +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include + +#include "cudaq/utils/tensor.h" +#include "cudaq/operators.h" +#include "boson_operators.h" + +namespace cudaq { + +// read-only properties + +std::vector boson_operator::degrees() const { + return {this->target}; +} + +// constructors + +boson_operator::boson_operator(int op_id, int target) + : id(op_id), target(target) { + assert(0 <= op_id < 4); +} + +bool boson_operator::is_identity() const { + return this->id == 0; +} + +// evaluations + +matrix_2 boson_operator::to_matrix(std::map &dimensions, + std::map> parameters) const { + auto it = dimensions.find(this->target); + if (it == dimensions.end()) + throw std::runtime_error("missing dimension for degree " + std::to_string(this->target)); + auto dim = it->second; + + auto mat = matrix_2(dim, dim); + if (this->id == 1) { // create + for (std::size_t i = 0; i + 1 < dim; i++) + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } else if (this->id == 2) { // annihilate + for (std::size_t i = 0; i + 1 < dim; i++) + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; + } else if (this->id == 3) { // number + for (std::size_t i = 0; i < dim; i++) + mat[{i, i}] = static_cast(i) + 0.0j; + } else { // id + mat[{0, 0}] = 1.0; + mat[{1, 1}] = 1.0; + } + return mat; +} + +std::string boson_operator::to_string(bool include_degrees) const { + std::string op_str; + if (this->id == 1) op_str = "create"; + else if (this->id == 2) op_str = "annihilate"; + else if (this->id == 3) op_str = "number"; + else op_str = "identity"; + if (include_degrees) return op_str + "(" + std::to_string(target) + ")"; + else return op_str; +} + +// comparisons + +bool boson_operator::operator==(const boson_operator &other) const { + return this->id == other.id && this->target == other.target; +} + +// defined operators + +// multiplicative identity +boson_operator boson_operator::one(int degree) { + return boson_operator(0, degree); +} + +product_operator boson_operator::identity(int degree) { + return product_operator(boson_operator(0, degree)); +} + +product_operator boson_operator::create(int degree) { + return product_operator(boson_operator(1, degree)); +} + +product_operator boson_operator::annihilate(int degree) { + return product_operator(boson_operator(2, degree)); +} + +product_operator boson_operator::number(int degree) { + return product_operator(boson_operator(3, degree)); +} + +// FIXME: add position, momentum, others? + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h new file mode 100644 index 0000000000..6c7bc0dc5b --- /dev/null +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "cudaq/utils/tensor.h" +#include "cudaq/operators.h" + +namespace cudaq { + +template +class product_operator; + +// FIXME: rename? +class boson_operator : operator_handler{ + +private: + + // ... + int id; + int target; + + boson_operator(int op, int target); + +public: + + // read-only properties + + /// @brief The degrees of freedom that the operator acts on in canonical + /// order. + virtual std::vector degrees() const; + + virtual bool is_identity() const; + + // constructors and destructors + + ~boson_operator() = default; + + // assignments + + // evaluations + + /// @brief Return the `matrix_operator` as a matrix. + /// @arg `dimensions` : A map specifying the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0 : 2, 1 : 2}`. + virtual matrix_2 to_matrix(std::map &dimensions, + std::map> parameters = {}) const; + + virtual std::string to_string(bool include_degrees) const; + + // comparisons + + bool operator==(const boson_operator &other) const; + + // defined operators + + // multiplicative identity + static boson_operator one(int degree); + static product_operator identity(int degree); + static product_operator create(int degree); + static product_operator annihilate(int degree); + static product_operator number(int degree); +}; + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 454815a1e4..7cad248f61 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -6,13 +6,15 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include +#include +#include + +#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" #include "matrix_operators.h" #include "spin_operators.h" - -#include -#include -#include +#include "boson_operators.h" namespace cudaq { @@ -29,14 +31,14 @@ void matrix_operator::define(std::string operator_id, std::vector expected_ } } -product_operator matrix_operator::create(std::string operator_id, const std::vector °rees) { +product_operator matrix_operator::instantiate(std::string operator_id, const std::vector °rees) { auto it = matrix_operator::m_ops.find(operator_id); if (it == matrix_operator::m_ops.end()) throw std::range_error("not matrix operator with the name '" + operator_id + "' has been defined"); return product_operator(matrix_operator(operator_id, degrees)); } -product_operator matrix_operator::create(std::string operator_id, std::vector &°rees) { +product_operator matrix_operator::instantiate(std::string operator_id, std::vector &°rees) { auto it = matrix_operator::m_ops.find(operator_id); if (it == matrix_operator::m_ops.end()) throw std::range_error("not matrix operator with the name '" + operator_id + "' has been defined"); @@ -84,6 +86,7 @@ matrix_operator::matrix_operator(const T &other) { } template matrix_operator::matrix_operator(const spin_operator &other); +template matrix_operator::matrix_operator(const boson_operator &other); matrix_operator::matrix_operator(const matrix_operator &other) : targets(other.targets), id(other.id) {} @@ -108,6 +111,7 @@ matrix_operator& matrix_operator::operator=(const T& other) { } template matrix_operator& matrix_operator::operator=(const spin_operator& other); +template matrix_operator& matrix_operator::operator=(const boson_operator& other); matrix_operator& matrix_operator::operator=(matrix_operator &&other) { if (this != &other) { diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index 95e062769e..f8c89d5194 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -8,12 +8,18 @@ #pragma once +#include #include #include + +#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" namespace cudaq { +template +class product_operator; + class matrix_operator : operator_handler{ private: @@ -64,12 +70,12 @@ class matrix_operator : operator_handler{ /// @brief Instantiates a custom operator. /// @arg operator_id : The ID of the operator as specified when it was defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - static product_operator create(std::string operator_id, const std::vector °rees); + static product_operator instantiate(std::string operator_id, const std::vector °rees); /// @brief Instantiates a custom operator. /// @arg operator_id : The ID of the operator as specified when it was defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - static product_operator create(std::string operator_id, std::vector &°rees); + static product_operator instantiate(std::string operator_id, std::vector &°rees); // read-only properties @@ -137,12 +143,4 @@ class matrix_operator : operator_handler{ static product_operator displace(int degree); }; -#ifdef CUDAQ_INSTANTIATE_TEMPLATES -template class product_operator; -template class operator_sum; -#else -extern template class product_operator; -extern template class operator_sum; -#endif - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h new file mode 100644 index 0000000000..e8b08cf2ed --- /dev/null +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -0,0 +1,148 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +#include "callback.h" +#include "cudaq/utils/tensor.h" + +namespace cudaq { + +class scalar_operator { + +private: + // If someone gave us a constant value, we will just return that + // directly to them when they call `evaluate`. + std::variant, ScalarCallbackFunction> value; + +public: + + // constructors and destructors + + scalar_operator(double value); + + /// @brief Constructor that just takes and returns a complex double value. + /// @NOTE: This replicates the behavior of the python `scalar_operator::const` + /// without the need for an extra member function. + scalar_operator(std::complex value); + + scalar_operator(const ScalarCallbackFunction &create); + + /// @brief Constructor that just takes a callback function with no + /// arguments. + scalar_operator(ScalarCallbackFunction &&create); + + // copy constructor + scalar_operator(const scalar_operator &other); + + // move constructor + scalar_operator(scalar_operator &&other); + + ~scalar_operator() = default; + + // assignments + + // assignment operator + scalar_operator& operator=(const scalar_operator &other); + + // move assignment operator + scalar_operator& operator=(scalar_operator &&other); + + // evaluations + + /// @brief Return the scalar operator as a concrete complex value. + std::complex + evaluate(const std::map> parameters = {}) const; + + // Return the scalar operator as a 1x1 matrix. This is needed for + // compatibility with the other inherited classes. + matrix_2 to_matrix(const std::map dimensions = {}, + const std::map> parameters = {}) const; + + // comparisons + + bool operator==(scalar_operator other); + + // unary operators + + scalar_operator operator-() const; + scalar_operator operator+() const; + + // right-hand arithmetics + + scalar_operator operator*(double other) const; + scalar_operator operator/(double other) const; + scalar_operator operator+(double other) const; + scalar_operator operator-(double other) const; + scalar_operator& operator*=(double other); + scalar_operator& operator/=(double other); + scalar_operator& operator+=(double other); + scalar_operator& operator-=(double other); + scalar_operator operator*(std::complex other) const; + scalar_operator operator/(std::complex other) const; + scalar_operator operator+(std::complex other) const; + scalar_operator operator-(std::complex other) const; + scalar_operator& operator*=(std::complex other); + scalar_operator& operator/=(std::complex other); + scalar_operator& operator+=(std::complex other); + scalar_operator& operator-=(std::complex other); + scalar_operator operator*(const scalar_operator &other) const; + scalar_operator operator/(const scalar_operator &other) const; + scalar_operator operator+(const scalar_operator &other) const; + scalar_operator operator-(const scalar_operator &other) const; + scalar_operator& operator*=(const scalar_operator &other); + scalar_operator& operator/=(const scalar_operator &other); + scalar_operator& operator+=(const scalar_operator &other); + scalar_operator& operator-=(const scalar_operator &other); + + friend scalar_operator operator*(scalar_operator &&self, double other); + friend scalar_operator operator/(scalar_operator &&self, double other); + friend scalar_operator operator+(scalar_operator &&self, double other); + friend scalar_operator operator-(scalar_operator &&self, double other); + friend scalar_operator operator+(scalar_operator &&self, std::complex other); + friend scalar_operator operator/(scalar_operator &&self, std::complex other); + friend scalar_operator operator+(scalar_operator &&self, std::complex other); + friend scalar_operator operator-(scalar_operator &&self, std::complex other); + + // left-hand arithmetics + + friend scalar_operator operator*(double other, const scalar_operator &self); + friend scalar_operator operator/(double other, const scalar_operator &self); + friend scalar_operator operator+(double other, const scalar_operator &self); + friend scalar_operator operator-(double other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, const scalar_operator &self); + friend scalar_operator operator/(std::complex other, const scalar_operator &self); + friend scalar_operator operator+(std::complex other, const scalar_operator &self); + friend scalar_operator operator-(std::complex other, const scalar_operator &self); +}; + +class operator_handler { +public: + virtual ~operator_handler() = default; + + virtual std::vector degrees() const = 0; + + virtual bool is_identity() const = 0; + + /// @brief Return the `matrix_operator` as a matrix. + /// @arg `dimensions` : A map specifying the number of levels, + /// that is, the dimension of each degree of freedom + /// that the operator acts on. Example for two, 2-level + /// degrees of freedom: `{0 : 2, 1 : 2}`. + virtual matrix_2 to_matrix(std::map &dimensions, + std::map> parameters = {}) const = 0; + + virtual std::string to_string(bool include_degrees = true) const = 0; +}; + +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 71b9f6dfeb..25c2c8ef9c 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -6,17 +6,17 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/helpers.h" +#include +#include +#include +#include + #include "cudaq/operators.h" #include "helpers.h" #include "manipulation.h" #include "matrix_operators.h" #include "spin_operators.h" - -#include -#include -#include -#include +#include "boson_operators.h" namespace cudaq { @@ -94,6 +94,7 @@ void operator_sum::aggregate_terms(product_operator &&head INSTANTIATE_SUM_PRIVATE_METHODS(matrix_operator); INSTANTIATE_SUM_PRIVATE_METHODS(spin_operator); +INSTANTIATE_SUM_PRIVATE_METHODS(boson_operator); // read-only properties @@ -140,6 +141,7 @@ operator_sum::get_terms() const { INSTANTIATE_SUM_PROPERTIES(matrix_operator); INSTANTIATE_SUM_PROPERTIES(spin_operator); +INSTANTIATE_SUM_PROPERTIES(boson_operator); // constructors @@ -230,9 +232,12 @@ operator_sum::operator_sum(operator_sum &&other) template operator_sum::operator_sum(const operator_sum &other); +template +operator_sum::operator_sum(const operator_sum &other); INSTANTIATE_SUM_CONSTRUCTORS(matrix_operator); INSTANTIATE_SUM_CONSTRUCTORS(spin_operator); +INSTANTIATE_SUM_CONSTRUCTORS(boson_operator); // assignments @@ -274,9 +279,12 @@ operator_sum::operator=(operator_sum &&other) { template operator_sum& operator_sum::operator=(const operator_sum &other); +template +operator_sum& operator_sum::operator=(const operator_sum &other); INSTANTIATE_SUM_ASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_ASSIGNMENTS(spin_operator); +INSTANTIATE_SUM_ASSIGNMENTS(boson_operator); // evaluations @@ -303,6 +311,7 @@ matrix_2 operator_sum::to_matrix(const std::map &dimensions INSTANTIATE_SUM_EVALUATIONS(matrix_operator); INSTANTIATE_SUM_EVALUATIONS(spin_operator); +INSTANTIATE_SUM_EVALUATIONS(boson_operator); // comparisons @@ -319,6 +328,7 @@ bool operator_sum::operator==( INSTANTIATE_SUM_COMPARISONS(matrix_operator); INSTANTIATE_SUM_COMPARISONS(spin_operator); +INSTANTIATE_SUM_COMPARISONS(boson_operator); // unary operators @@ -349,6 +359,7 @@ operator_sum operator_sum::operator+() const { INSTANTIATE_SUM_UNARY_OPS(matrix_operator); INSTANTIATE_SUM_UNARY_OPS(spin_operator); +INSTANTIATE_SUM_UNARY_OPS(boson_operator); // right-hand arithmetics @@ -470,6 +481,7 @@ SUM_ADDITION_HANDLER(-) INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); +INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template operator_sum operator_sum::operator*( @@ -595,6 +607,7 @@ SUM_ADDITION_SUM(-); INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); +INSTANTIATE_SUM_RHCOMPOSITE_OPS(boson_operator); #define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -739,6 +752,7 @@ SUM_ADDITION_SUM_ASSIGNMENT(-); INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); +INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); // left-hand arithmetics @@ -860,5 +874,87 @@ SUM_ADDITION_HANDLER_REVERSE(-) INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); +INSTANTIATE_SUM_LHCOMPOSITE_OPS(boson_operator); + +// arithmetics that require conversions + +#define SUM_CONVERSIONS_OPS(op) \ + \ + template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self) { \ + return operator_sum(other) op self; \ + } \ + \ + template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self) { \ + return product_operator(other) op self; \ + } \ + \ + template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self) { \ + return operator_sum(other) op self; \ + } + +SUM_CONVERSIONS_OPS(*); +SUM_CONVERSIONS_OPS(+); +SUM_CONVERSIONS_OPS(-); + +#define INSTANTIATE_SUM_CONVERSION_OPS(op) \ + \ + template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + \ + template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + \ + template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + +INSTANTIATE_SUM_CONVERSION_OPS(*); +INSTANTIATE_SUM_CONVERSION_OPS(+); +INSTANTIATE_SUM_CONVERSION_OPS(-); + + +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +template class operator_sum; +template class operator_sum; +template class operator_sum; +#endif } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 44449dbbc7..e93c017328 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -6,17 +6,17 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/helpers.h" +#include +#include +#include +#include + #include "cudaq/operators.h" #include "helpers.h" #include "manipulation.h" #include "matrix_operators.h" #include "spin_operators.h" - -#include -#include -#include -#include +#include "boson_operators.h" namespace cudaq { @@ -100,6 +100,7 @@ EvaluatedMatrix product_operator::m_evaluate( INSTANTIATE_PRODUCT_PRIVATE_METHODS(matrix_operator); INSTANTIATE_PRODUCT_PRIVATE_METHODS(spin_operator); +INSTANTIATE_PRODUCT_PRIVATE_METHODS(boson_operator); // read-only properties @@ -146,6 +147,7 @@ scalar_operator product_operator::get_coefficient() const { INSTANTIATE_PRODUCT_PROPERTIES(matrix_operator); INSTANTIATE_PRODUCT_PROPERTIES(spin_operator); +INSTANTIATE_PRODUCT_PROPERTIES(boson_operator); // constructors @@ -236,9 +238,12 @@ product_operator::product_operator(product_operator &&othe template product_operator::product_operator(const product_operator &other); +template +product_operator::product_operator(const product_operator &other); INSTANTIATE_PRODUCT_CONSTRUCTORS(matrix_operator); INSTANTIATE_PRODUCT_CONSTRUCTORS(spin_operator); +INSTANTIATE_PRODUCT_CONSTRUCTORS(boson_operator); // assignments @@ -280,9 +285,12 @@ product_operator::operator=(product_operator &&other) { template product_operator& product_operator::operator=(const product_operator &other); +template +product_operator& product_operator::operator=(const product_operator &other); INSTANTIATE_PRODUCT_ASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_ASSIGNMENTS(spin_operator); +INSTANTIATE_PRODUCT_ASSIGNMENTS(boson_operator); // evaluations @@ -309,6 +317,7 @@ matrix_2 product_operator::to_matrix(std::map dimensions, INSTANTIATE_PRODUCT_EVALUATIONS(matrix_operator); INSTANTIATE_PRODUCT_EVALUATIONS(spin_operator); +INSTANTIATE_PRODUCT_EVALUATIONS(boson_operator); // comparisons @@ -326,6 +335,7 @@ bool product_operator::operator==( INSTANTIATE_PRODUCT_COMPARISONS(matrix_operator); INSTANTIATE_PRODUCT_COMPARISONS(spin_operator); +INSTANTIATE_PRODUCT_COMPARISONS(boson_operator); // unary operators @@ -349,6 +359,7 @@ product_operator product_operator::operator+() const { INSTANTIATE_PRODUCT_UNARY_OPS(matrix_operator); INSTANTIATE_PRODUCT_UNARY_OPS(spin_operator); +INSTANTIATE_PRODUCT_UNARY_OPS(boson_operator); // right-hand arithmetics @@ -429,6 +440,7 @@ PRODUCT_ADDITION_HANDLER(-) INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); +INSTANTIATE_PRODUCT_RHSIMPLE_OPS(boson_operator); template product_operator product_operator::operator*( @@ -522,6 +534,7 @@ PRODUCT_ADDITION_SUM(-) INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(spin_operator); +INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(boson_operator); #define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ template \ @@ -563,6 +576,7 @@ product_operator& product_operator::operator*=(const produ INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); +INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); // left-hand arithmetics @@ -642,5 +656,46 @@ PRODUCT_ADDITION_HANDLER_REVERSE(-) INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); +INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(boson_operator); + +// arithmetics that require conversions + +#define PRODUCT_CONVERSIONS_OPS(op, returnTy) \ + template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self) { \ + return product_operator(other) op self; \ + } + +PRODUCT_CONVERSIONS_OPS(*, product_operator); +PRODUCT_CONVERSIONS_OPS(+, operator_sum); +PRODUCT_CONVERSIONS_OPS(-, operator_sum); + +#define INSTANTIATE_PRODUCT_CONVERSION_OPS(op, returnTy) \ + \ + template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); + +INSTANTIATE_PRODUCT_CONVERSION_OPS(*, product_operator); +INSTANTIATE_PRODUCT_CONVERSION_OPS(+, operator_sum); +INSTANTIATE_PRODUCT_CONVERSION_OPS(-, operator_sum); + + +#ifdef CUDAQ_INSTANTIATE_TEMPLATES +template class product_operator; +template class product_operator; +template class product_operator; +#endif } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index af29f51d90..d2859314eb 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -6,10 +6,13 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/operators.h" -#include "spin_operators.h" +#include +#include #include +#include "cudaq/utils/tensor.h" +#include "spin_operators.h" + namespace cudaq { // read-only properties diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 4fef23e4c5..043d210473 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -8,15 +8,18 @@ #pragma once +#include #include #include -#include "cudaq/operators.h" -#include "cudaq/utils/tensor.h" -#include +#include "cudaq/utils/tensor.h" +#include "cudaq/operators.h" namespace cudaq { +template +class product_operator; + // FIXME: rename to spin ... class spin_operator : operator_handler{ @@ -70,12 +73,4 @@ class spin_operator : operator_handler{ static product_operator y(int degree); }; -#ifdef CUDAQ_INSTANTIATE_TEMPLATES -template class product_operator; -template class operator_sum; -#else -extern template class product_operator; -extern template class operator_sum; -#endif - } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 7fea2dd812..43aab3c128 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -9,9 +9,12 @@ #include #include -namespace cudaq { +#include "operator_leafs.h" +#include "matrix_operators.h" +#include "spin_operators.h" +#include "boson_operators.h" -class scalar_operator; +namespace cudaq { template class product_operator; @@ -19,79 +22,88 @@ class product_operator; template class operator_sum; -template -product_operator operator*(double other, - const product_operator &self); -template -operator_sum operator+(double other, - const product_operator &self); -template -operator_sum operator-(double other, - const product_operator &self); -template -product_operator operator*(std::complex other, - const product_operator &self); -template -operator_sum operator+(std::complex other, - const product_operator &self); -template -operator_sum operator-(std::complex other, - const product_operator &self); -template -product_operator operator*(const scalar_operator &other, - const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, - const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, - const product_operator &self); -template -product_operator operator*(const HandlerTy &other, - const product_operator &self); -template -operator_sum operator+(const HandlerTy &other, - const product_operator &self); -template -operator_sum operator-(const HandlerTy &other, - const product_operator &self); +#define TYPE_CONVERSION_CONSTRAINT(LHtype, RHtype) \ + std::enable_if_t::value && \ + !std::is_same::value && \ + std::is_base_of::value && \ + std::is_base_of::value, bool> + +template +product_operator operator*(double other, const product_operator &self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator-(double other, const product_operator &self); +template +product_operator operator*(std::complex other, const product_operator &self); +template +operator_sum operator+(std::complex other, const product_operator &self); +template +operator_sum operator-(std::complex other, const product_operator &self); +template +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(const HandlerTy &other, const product_operator &self); +template +operator_sum operator+(const HandlerTy &other, const product_operator &self); +template +operator_sum operator-(const HandlerTy &other, const product_operator &self); + +template +product_operator operator*(const product_operator &other, const product_operator &self); +template +operator_sum operator+(const product_operator &other, const product_operator &self); +template +operator_sum operator-(const product_operator &other, const product_operator &self); + +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator-(double other, const operator_sum &self); +template +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator+(std::complex other, const operator_sum &self); +template +operator_sum operator-(std::complex other, const operator_sum &self); +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator*(const HandlerTy &other, const operator_sum &self); +template +operator_sum operator+(const HandlerTy &other, const operator_sum &self); +template +operator_sum operator-(const HandlerTy &other, const operator_sum &self); + +template +operator_sum operator*(const operator_sum &other, const product_operator &self); +template +operator_sum operator+(const operator_sum &other, const product_operator &self); +template +operator_sum operator-(const operator_sum &other, const product_operator &self); +template +operator_sum operator*(const product_operator &other, const operator_sum &self); +template +operator_sum operator+(const product_operator &other, const operator_sum &self); +template +operator_sum operator-(const product_operator &other, const operator_sum &self); +template +operator_sum operator*(const operator_sum &other, const operator_sum &self); +template +operator_sum operator+(const operator_sum &other, const operator_sum &self); +template +operator_sum operator-(const operator_sum &other, const operator_sum &self); -template -operator_sum operator*(double other, - const operator_sum &self); -template -operator_sum operator+(double other, - const operator_sum &self); -template -operator_sum operator-(double other, - const operator_sum &self); -template -operator_sum operator*(std::complex other, - const operator_sum &self); -template -operator_sum operator+(std::complex other, - const operator_sum &self); -template -operator_sum operator-(std::complex other, - const operator_sum &self); -template -operator_sum operator*(const scalar_operator &other, - const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, - const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, - const operator_sum &self); -template -operator_sum operator*(const HandlerTy &other, - const operator_sum &self); -template -operator_sum operator+(const HandlerTy &other, - const operator_sum &self); -template -operator_sum operator-(const HandlerTy &other, - const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES #define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ @@ -146,10 +158,67 @@ operator_sum operator-(const HandlerTy &other, extern template \ operator_sum operator-(const HandlerTy &other, const operator_sum &self); -class matrix_operator; -class spin_operator; EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); +EXTERN_TEMPLATE_SPECIALIZATIONS(boson_operator); + +#define EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(op, returnTy) \ + \ + extern template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + extern template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + extern template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + extern template \ + returnTy operator op(const product_operator &other, \ + const product_operator &self); \ + \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const product_operator &self); \ + \ + extern template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + extern template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + extern template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + extern template \ + operator_sum operator op(const product_operator &other, \ + const operator_sum &self); \ + \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + extern template \ + operator_sum operator op(const operator_sum &other, \ + const operator_sum &self); \ + +EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(*, product_operator); +EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(+, operator_sum); +EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(-, operator_sum); #endif } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index dbaaffd856..bd3a108ebf 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,141 +8,18 @@ #pragma once -#include "dynamics/templates.h" -#include "dynamics/callback.h" -#include "utils/tensor.h" - -#include -#include -#include #include -#include #include +#include "utils/tensor.h" +#include "dynamics/operator_leafs.h" +#include "dynamics/templates.h" + namespace cudaq { class MatrixArithmetics; class EvaluatedMatrix; -class scalar_operator { - -private: - // If someone gave us a constant value, we will just return that - // directly to them when they call `evaluate`. - std::variant, ScalarCallbackFunction> value; - -public: - // constructors and destructors - - scalar_operator(double value); - - /// @brief Constructor that just takes and returns a complex double value. - /// @NOTE: This replicates the behavior of the python `scalar_operator::const` - /// without the need for an extra member function. - scalar_operator(std::complex value); - - scalar_operator(const ScalarCallbackFunction &create); - - /// @brief Constructor that just takes a callback function with no - /// arguments. - scalar_operator(ScalarCallbackFunction &&create); - - // copy constructor - scalar_operator(const scalar_operator &other); - - // move constructor - scalar_operator(scalar_operator &&other); - - ~scalar_operator() = default; - - // assignments - - // assignment operator - scalar_operator &operator=(const scalar_operator &other); - - // move assignment operator - scalar_operator &operator=(scalar_operator &&other); - - // evaluations - - /// @brief Return the scalar operator as a concrete complex value. - std::complex evaluate( - const std::map> parameters = {}) const; - - ScalarCallbackFunction get_generator() const; - - // Return the scalar operator as a 1x1 matrix. This is needed for - // compatibility with the other inherited classes. - matrix_2 to_matrix( - const std::map dimensions = {}, - const std::map> parameters = {}) const; - - // comparisons - - bool operator==(scalar_operator other); - - // unary operators - - scalar_operator operator-() const; - scalar_operator operator+() const; - - // right-hand arithmetics - - scalar_operator operator*(double other) const; - scalar_operator operator/(double other) const; - scalar_operator operator+(double other) const; - scalar_operator operator-(double other) const; - scalar_operator &operator*=(double other); - scalar_operator &operator/=(double other); - scalar_operator &operator+=(double other); - scalar_operator &operator-=(double other); - scalar_operator operator*(std::complex other) const; - scalar_operator operator/(std::complex other) const; - scalar_operator operator+(std::complex other) const; - scalar_operator operator-(std::complex other) const; - scalar_operator &operator*=(std::complex other); - scalar_operator &operator/=(std::complex other); - scalar_operator &operator+=(std::complex other); - scalar_operator &operator-=(std::complex other); - scalar_operator operator*(const scalar_operator &other) const; - scalar_operator operator/(const scalar_operator &other) const; - scalar_operator operator+(const scalar_operator &other) const; - scalar_operator operator-(const scalar_operator &other) const; - scalar_operator &operator*=(const scalar_operator &other); - scalar_operator &operator/=(const scalar_operator &other); - scalar_operator &operator+=(const scalar_operator &other); - scalar_operator &operator-=(const scalar_operator &other); - /// TODO: implement and test pow - - friend scalar_operator operator*(scalar_operator &&self, double other); - friend scalar_operator operator/(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, double other); - friend scalar_operator operator-(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, - std::complex other); - friend scalar_operator operator/(scalar_operator &&self, - std::complex other); - friend scalar_operator operator+(scalar_operator &&self, - std::complex other); - friend scalar_operator operator-(scalar_operator &&self, - std::complex other); - - // left-hand arithmetics - - friend scalar_operator operator*(double other, const scalar_operator &self); - friend scalar_operator operator/(double other, const scalar_operator &self); - friend scalar_operator operator+(double other, const scalar_operator &self); - friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, - const scalar_operator &self); - friend scalar_operator operator/(std::complex other, - const scalar_operator &self); - friend scalar_operator operator+(std::complex other, - const scalar_operator &self); - friend scalar_operator operator-(std::complex other, - const scalar_operator &self); -}; - /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -522,25 +399,15 @@ template friend class operator_sum; const product_operator &self); }; +#ifndef CUDAQ_INSTANTIATE_TEMPLATES +extern template class product_operator; +extern template class product_operator; +extern template class product_operator; -class operator_handler { -public: - virtual ~operator_handler() = default; - - virtual std::vector degrees() const = 0; - - virtual bool is_identity() const = 0; - - /// @brief Return the `matrix_operator` as a matrix. - /// @arg `dimensions` : A map specifying the number of levels, - /// that is, the dimension of each degree of freedom - /// that the operator acts on. Example for two, 2-level - /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map &dimensions, - std::map> parameters = {}) const = 0; - - virtual std::string to_string(bool include_degrees = true) const = 0; -}; +extern template class operator_sum; +extern template class operator_sum; +extern template class operator_sum; +#endif /// @brief Representation of a time-dependent Hamiltonian for Rydberg system class rydberg_hamiltonian { diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 2e40b96a44..e3b3396e68 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -141,8 +141,8 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { // op 1: // number level on 3 // create level+2 on 1 - auto op0 = cudaq::matrix_operator::create("custom_op0", {0, 1}); - auto op1 = cudaq::matrix_operator::create("custom_op1", {1, 3}); + auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 3}); auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), utils::position_matrix(level_count + 2)); @@ -682,8 +682,8 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } - auto custom_op0 = cudaq::matrix_operator::create("custom_op0", {3, 1}); - auto custom_op1 = cudaq::matrix_operator::create("custom_op1", {1, 0}); + auto custom_op0 = cudaq::matrix_operator::instantiate("custom_op0", {3, 1}); + auto custom_op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 0}); ASSERT_THROW(op1.to_matrix(), std::runtime_error); ASSERT_THROW(op1.to_matrix({{1, 2}}), std::runtime_error); diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 4f23398953..31dc551215 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -1300,8 +1300,8 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } - auto op0 = cudaq::matrix_operator::create("custom_op0", {0, 1}); - auto op1 = cudaq::matrix_operator::create("custom_op1", {1, 2}); + auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); auto sum = op0 + op1; auto sum_reverse = op1 + op0; auto difference = op0 - op1; @@ -1327,8 +1327,8 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { utils::checkEqual(difference.to_matrix(dimensions), diff_expected); utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); - op0 = cudaq::matrix_operator::create("custom_op0", {2, 3}); - op1 = cudaq::matrix_operator::create("custom_op1", {2, 0}); + op0 = cudaq::matrix_operator::instantiate("custom_op0", {2, 3}); + op1 = cudaq::matrix_operator::instantiate("custom_op1", {2, 0}); sum = op0 + op1; sum_reverse = op1 + op0; difference = op0 - op1; diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index 2f030956b3..26467dc9ec 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -824,18 +824,28 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { // `product_operator + spin_operator` { + auto mp = cudaq::matrix_operator::annihilate(0); + auto me = mp.get_terms()[0]; + auto sp = cudaq::spin_operator::x(1) * cudaq::spin_operator::x(1); + //auto fp = cudaq::fermion_operator::? + //auto s = me + sp; + auto s = mp + sp; + //auto r = sp + me; + auto r = sp + mp; + // auto r = fp + sp; auto r = sp + fp; + auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); auto elementary = cudaq::spin_operator::x(1); auto sum = product + elementary; - //auto reverse = elementary + product; + auto reverse = elementary + product; ASSERT_TRUE(sum.n_terms() == 2); - //ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(reverse.n_terms() == 2); auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - //auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); auto product_matrix = cudaq::kronecker(utils::id_matrix(level_count), @@ -849,7 +859,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { auto want_matrix_reverse = elementary_matrix + product_matrix; utils::checkEqual(want_matrix, got_matrix); - //utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } // `product_operator - matrix_operator` @@ -1569,8 +1579,8 @@ TEST(OperatorExpressions, checkCustomProductOps) { cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } - auto op0 = cudaq::matrix_operator::create("custom_op0", {0, 1}); - auto op1 = cudaq::matrix_operator::create("custom_op1", {1, 2}); + auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); auto product = op0 * op1; auto reverse = op1 * op0; @@ -1589,8 +1599,8 @@ TEST(OperatorExpressions, checkCustomProductOps) { utils::checkEqual(product.to_matrix(dimensions), expected); utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); - op0 = cudaq::matrix_operator::create("custom_op0", {2, 3}); - op1 = cudaq::matrix_operator::create("custom_op1", {2, 0}); + op0 = cudaq::matrix_operator::instantiate("custom_op0", {2, 3}); + op1 = cudaq::matrix_operator::instantiate("custom_op1", {2, 0}); product = op0 * op1; reverse = op1 * op0; From 1377a2f05c883b28a3ad4f82e06502103d18e513 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 10 Feb 2025 15:25:02 +0000 Subject: [PATCH 262/311] finished conversion tests Signed-off-by: Bettina Heim --- unittests/CMakeLists.txt | 7 +- unittests/dynamics/matrix_operator.cpp | 15 + unittests/dynamics/operator_conversions.cpp | 478 ++++++++++++++++++++ unittests/dynamics/product_operator.cpp | 166 ------- 4 files changed, 497 insertions(+), 169 deletions(-) create mode 100644 unittests/dynamics/operator_conversions.cpp diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index d3b99bcf3c..46f4c14d2e 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -265,11 +265,12 @@ gtest_discover_tests(test_spin) # Create an executable for operators UnitTests set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/utils.cpp - dynamics/operator_sum.cpp - dynamics/spin_operator.cpp - dynamics/matrix_operator.cpp dynamics/scalar_operator.cpp + dynamics/matrix_operator.cpp + dynamics/spin_operator.cpp + dynamics/operator_conversions.cpp dynamics/product_operator.cpp + dynamics/operator_sum.cpp ) add_executable(test_operators main.cpp ${CUDAQ_OPERATOR_TEST_SOURCES}) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index e3b3396e68..9a6d5fd1c6 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -700,3 +700,18 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { ASSERT_NO_THROW((custom_op0 + custom_op1).to_matrix(dimensions)); } +TEST(OperatorExpressions, checkMatrixOpsParameterVerification) { + + std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::map dimensions = {{0, 2}, {1, 2}}; + + auto squeeze = cudaq::matrix_operator::squeeze(1); + auto displace = cudaq::matrix_operator::displace(0); + + ASSERT_THROW(squeeze.to_matrix(dimensions), std::runtime_error); + ASSERT_THROW(squeeze.to_matrix(dimensions, {{"displacement", 0.25}}), std::runtime_error); + ASSERT_THROW((squeeze * displace).to_matrix(dimensions, {{"displacement", 0.25}}), std::runtime_error); + ASSERT_THROW((squeeze + displace).to_matrix(dimensions, {{"squeezing", 0.5}}), std::runtime_error); + ASSERT_NO_THROW((squeeze * displace).to_matrix(dimensions, parameters)); + ASSERT_NO_THROW((squeeze + displace).to_matrix(dimensions, parameters)); +} diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp new file mode 100644 index 0000000000..065d7cf2e6 --- /dev/null +++ b/unittests/dynamics/operator_conversions.cpp @@ -0,0 +1,478 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "utils.h" +#include "cudaq/operators.h" +#include + +TEST(OperatorExpressions, checkElementaryOpsConversions) { + + std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::map dimensions = {{0, 2}, {1, 2}}; + + auto matrix_elementary = cudaq::matrix_operator::parity(1); + auto matrix_elementary_expected = utils::parity_matrix(2); + auto spin_elementary = cudaq::spin_operator::y(1); + auto spin_elementary_expected = utils::PauliY_matrix(); + auto boson_elementary = cudaq::boson_operator::annihilate(1); + auto boson_elementary_expected = utils::annihilate_matrix(2); + + auto checkSumEquals = [dimensions, parameters]( + cudaq::operator_sum sum, + cudaq::matrix_2 expected) { + auto got = sum.to_matrix(dimensions, parameters); + ASSERT_TRUE(sum.n_terms() == 2); + utils::checkEqual(got, expected); + }; + + auto checkProductEquals = [dimensions, parameters]( + cudaq::product_operator prod, + cudaq::matrix_2 expected) { + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.n_terms() == 2); + utils::checkEqual(got, expected); + }; + + // `elementary + elementary` + { + checkSumEquals(matrix_elementary + matrix_elementary, matrix_elementary_expected + matrix_elementary_expected); + checkSumEquals(spin_elementary + spin_elementary, spin_elementary_expected + spin_elementary_expected); + checkSumEquals(boson_elementary + boson_elementary, boson_elementary_expected + boson_elementary_expected); + checkSumEquals(matrix_elementary + spin_elementary, matrix_elementary_expected + spin_elementary_expected); + checkSumEquals(spin_elementary + matrix_elementary, matrix_elementary_expected + spin_elementary_expected); + checkSumEquals(matrix_elementary + boson_elementary, matrix_elementary_expected + boson_elementary_expected); + checkSumEquals(boson_elementary + matrix_elementary, matrix_elementary_expected + boson_elementary_expected); + checkSumEquals(spin_elementary + boson_elementary, spin_elementary_expected + boson_elementary_expected); + checkSumEquals(boson_elementary + spin_elementary, spin_elementary_expected + boson_elementary_expected); + } + + // `elementary - elementary` + { + checkSumEquals(matrix_elementary - matrix_elementary, matrix_elementary_expected - matrix_elementary_expected); + checkSumEquals(spin_elementary - spin_elementary, spin_elementary_expected - spin_elementary_expected); + checkSumEquals(boson_elementary - boson_elementary, boson_elementary_expected - boson_elementary_expected); + checkSumEquals(matrix_elementary - spin_elementary, matrix_elementary_expected - spin_elementary_expected); + checkSumEquals(spin_elementary - matrix_elementary, spin_elementary_expected - matrix_elementary_expected); + checkSumEquals(matrix_elementary - boson_elementary, matrix_elementary_expected - boson_elementary_expected); + checkSumEquals(boson_elementary - matrix_elementary, boson_elementary_expected - matrix_elementary_expected); + checkSumEquals(spin_elementary - boson_elementary, spin_elementary_expected - boson_elementary_expected); + checkSumEquals(boson_elementary - spin_elementary, boson_elementary_expected - spin_elementary_expected); + } + + // `elementary * elementary` + { + checkProductEquals(matrix_elementary * matrix_elementary, matrix_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * spin_elementary, spin_elementary_expected * spin_elementary_expected); + checkProductEquals(boson_elementary * boson_elementary, boson_elementary_expected * boson_elementary_expected); + checkProductEquals(matrix_elementary * spin_elementary, matrix_elementary_expected * spin_elementary_expected); + checkProductEquals(spin_elementary * matrix_elementary, spin_elementary_expected * matrix_elementary_expected); + checkProductEquals(matrix_elementary * boson_elementary, matrix_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * matrix_elementary, boson_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * boson_elementary, spin_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * spin_elementary, boson_elementary_expected * spin_elementary_expected); + } + + // `elementary *= elementary` + { + auto matrix_product = cudaq::product_operator(matrix_elementary); + matrix_product *= matrix_elementary; + checkProductEquals(matrix_product, matrix_elementary_expected * matrix_elementary_expected); + + auto spin_product = cudaq::product_operator(spin_elementary); + spin_product *= spin_elementary; + checkProductEquals(spin_product, spin_elementary_expected * spin_elementary_expected); + + auto boson_product = cudaq::product_operator(boson_elementary); + boson_product *= boson_elementary; + checkProductEquals(boson_product, boson_elementary_expected * boson_elementary_expected); + + matrix_product = cudaq::product_operator(matrix_elementary); + matrix_product *= spin_elementary; + checkProductEquals(matrix_product, matrix_elementary_expected * spin_elementary_expected); + + matrix_product = cudaq::product_operator(matrix_elementary); + matrix_product *= boson_elementary; + checkProductEquals(matrix_product, matrix_elementary_expected * boson_elementary_expected); + } +} + +TEST(OperatorExpressions, checkProductOperatorConversions) { + + std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::map dimensions = {{0, 2}, {1, 2}}; + auto matrix_product = cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); + auto matrix_product_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); + auto spin_product = cudaq::spin_operator::y(1) * cudaq::spin_operator::x(0); + auto spin_product_expected = cudaq::kronecker(utils::PauliY_matrix(), utils::PauliX_matrix()); + auto boson_product = cudaq::boson_operator::annihilate(1) * cudaq::boson_operator::number(0); + auto boson_product_expected = cudaq::kronecker(utils::annihilate_matrix(2), utils::number_matrix(2)); + + auto checkSumEquals = [dimensions, parameters]( + cudaq::operator_sum sum, + cudaq::matrix_2 expected) { + auto got = sum.to_matrix(dimensions, parameters); + ASSERT_TRUE(sum.n_terms() == 2); + utils::checkEqual(got, expected); + }; + + auto checkProductEquals = [dimensions, parameters]( + cudaq::product_operator prod, + cudaq::matrix_2 expected) { + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.n_terms() == 4); + utils::checkEqual(got, expected); + }; + + // `product + product` + { + checkSumEquals(matrix_product + matrix_product, matrix_product_expected + matrix_product_expected); + checkSumEquals(spin_product + spin_product, spin_product_expected + spin_product_expected); + checkSumEquals(boson_product + boson_product, boson_product_expected + boson_product_expected); + checkSumEquals(matrix_product + spin_product, matrix_product_expected + spin_product_expected); + checkSumEquals(spin_product + matrix_product, matrix_product_expected + spin_product_expected); + checkSumEquals(matrix_product + boson_product, matrix_product_expected + boson_product_expected); + checkSumEquals(boson_product + matrix_product, matrix_product_expected + boson_product_expected); + checkSumEquals(spin_product + boson_product, spin_product_expected + boson_product_expected); + checkSumEquals(boson_product + spin_product, spin_product_expected + boson_product_expected); + } + + // `product - product` + { + checkSumEquals(matrix_product - matrix_product, matrix_product_expected - matrix_product_expected); + checkSumEquals(spin_product - spin_product, spin_product_expected - spin_product_expected); + checkSumEquals(boson_product - boson_product, boson_product_expected - boson_product_expected); + checkSumEquals(matrix_product - spin_product, matrix_product_expected - spin_product_expected); + checkSumEquals(spin_product - matrix_product, spin_product_expected - matrix_product_expected); + checkSumEquals(matrix_product - boson_product, matrix_product_expected - boson_product_expected); + checkSumEquals(boson_product - matrix_product, boson_product_expected - matrix_product_expected); + checkSumEquals(spin_product - boson_product, spin_product_expected - boson_product_expected); + checkSumEquals(boson_product - spin_product, boson_product_expected - spin_product_expected); + } + + // `product * product` + { + checkProductEquals(matrix_product * matrix_product, matrix_product_expected * matrix_product_expected); + checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected); + checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected); + checkProductEquals(matrix_product * spin_product, matrix_product_expected * spin_product_expected); + checkProductEquals(spin_product * matrix_product, spin_product_expected * matrix_product_expected); + checkProductEquals(matrix_product * boson_product, matrix_product_expected * boson_product_expected); + checkProductEquals(boson_product * matrix_product, boson_product_expected * matrix_product_expected); + checkProductEquals(spin_product * boson_product, spin_product_expected * boson_product_expected); + checkProductEquals(boson_product * spin_product, boson_product_expected * spin_product_expected); + } + + // `product *= product` + { + auto matrix_product_0 = matrix_product; + matrix_product_0 *= matrix_product; + checkProductEquals(matrix_product_0, matrix_product_expected * matrix_product_expected); + + auto spin_product_0 = spin_product; + spin_product_0 *= spin_product; + checkProductEquals(spin_product_0, spin_product_expected * spin_product_expected); + + auto boson_product_0 = boson_product; + boson_product_0 *= boson_product; + checkProductEquals(boson_product_0, boson_product_expected * boson_product_expected); + + matrix_product_0 = matrix_product; + matrix_product_0 *= spin_product; + checkProductEquals(matrix_product_0, matrix_product_expected * spin_product_expected); + + matrix_product_0 = matrix_product; + matrix_product_0 *= boson_product; + checkProductEquals(matrix_product_0, matrix_product_expected * boson_product_expected); + } +} + +TEST(OperatorExpressions, checkOperatorSumConversions) { + + std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::map dimensions = {{0, 2}, {1, 2}}; + + auto matrix_product = cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); + auto matrix_product_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); + auto spin_product = cudaq::spin_operator::y(1) * cudaq::spin_operator::x(0); + auto spin_product_expected = cudaq::kronecker(utils::PauliY_matrix(), utils::PauliX_matrix()); + auto boson_product = cudaq::boson_operator::annihilate(1) * cudaq::boson_operator::number(0); + auto boson_product_expected = cudaq::kronecker(utils::annihilate_matrix(2), utils::number_matrix(2)); + + auto matrix_sum = cudaq::matrix_operator::squeeze(0) + cudaq::matrix_operator::displace(1); + auto matrix_sum_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::id_matrix(2)) + + cudaq::kronecker(utils::id_matrix(2), utils::squeeze_matrix(2, 0.5)); + auto spin_sum = cudaq::spin_operator::y(1) + cudaq::spin_operator::x(0); + auto spin_sum_expected = cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)) + + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto boson_sum = cudaq::boson_operator::annihilate(1) + cudaq::boson_operator::number(0); + auto boson_sum_expected = cudaq::kronecker(utils::annihilate_matrix(2), utils::id_matrix(2)) + + cudaq::kronecker(utils::id_matrix(2), utils::number_matrix(2)); + + auto checkSumEquals = [dimensions, parameters]( + cudaq::operator_sum sum, + cudaq::matrix_2 expected, int num_terms = 4) { + auto got = sum.to_matrix(dimensions, parameters); + ASSERT_TRUE(sum.n_terms() == num_terms); + utils::checkEqual(got, expected); + }; + + // `sum + product` + { + checkSumEquals(matrix_sum + matrix_product, matrix_sum_expected + matrix_product_expected, 3); + checkSumEquals(spin_sum + spin_product, spin_sum_expected + spin_product_expected, 3); + checkSumEquals(boson_sum + boson_product, boson_sum_expected + boson_product_expected, 3); + checkSumEquals(matrix_sum + spin_product, matrix_sum_expected + spin_product_expected, 3); + checkSumEquals(spin_sum + matrix_product, spin_sum_expected + matrix_product_expected, 3); + checkSumEquals(matrix_sum + boson_product, matrix_sum_expected + boson_product_expected, 3); + checkSumEquals(boson_sum + matrix_product, boson_sum_expected + matrix_product_expected, 3); + checkSumEquals(spin_sum + boson_product, spin_sum_expected + boson_product_expected, 3); + checkSumEquals(boson_sum + spin_product, boson_sum_expected + spin_product_expected, 3); + } + + // `product + sum` + { + checkSumEquals(matrix_product + matrix_sum, matrix_product_expected + matrix_sum_expected, 3); + checkSumEquals(spin_product + spin_sum, spin_product_expected + spin_sum_expected, 3); + checkSumEquals(boson_product + boson_sum, boson_product_expected + boson_sum_expected, 3); + checkSumEquals(matrix_product + spin_sum, matrix_product_expected + spin_sum_expected, 3); + checkSumEquals(spin_product + matrix_sum, spin_product_expected + matrix_sum_expected, 3); + checkSumEquals(matrix_product + boson_sum, matrix_product_expected + boson_sum_expected, 3); + checkSumEquals(boson_product + matrix_sum, boson_product_expected + matrix_sum_expected, 3); + checkSumEquals(spin_product + boson_sum, spin_product_expected + boson_sum_expected, 3); + checkSumEquals(boson_product + spin_sum, boson_product_expected + spin_sum_expected, 3); + } + + // `sum + sum` + { + checkSumEquals(matrix_sum + matrix_sum, matrix_sum_expected + matrix_sum_expected); + checkSumEquals(spin_sum + spin_sum, spin_sum_expected + spin_sum_expected); + checkSumEquals(boson_sum + boson_sum, boson_sum_expected + boson_sum_expected); + checkSumEquals(matrix_sum + spin_sum, matrix_sum_expected + spin_sum_expected); + checkSumEquals(spin_sum + matrix_sum, matrix_sum_expected + spin_sum_expected); + checkSumEquals(matrix_sum + boson_sum, matrix_sum_expected + boson_sum_expected); + checkSumEquals(boson_sum + matrix_sum, matrix_sum_expected + boson_sum_expected); + checkSumEquals(spin_sum + boson_sum, spin_sum_expected + boson_sum_expected); + checkSumEquals(boson_sum + spin_sum, spin_sum_expected + boson_sum_expected); + } + + // `sum - product` + { + checkSumEquals(matrix_sum - matrix_product, matrix_sum_expected - matrix_product_expected, 3); + checkSumEquals(spin_sum - spin_product, spin_sum_expected - spin_product_expected, 3); + checkSumEquals(boson_sum - boson_product, boson_sum_expected - boson_product_expected, 3); + checkSumEquals(matrix_sum - spin_product, matrix_sum_expected - spin_product_expected, 3); + checkSumEquals(spin_sum - matrix_product, spin_sum_expected - matrix_product_expected, 3); + checkSumEquals(matrix_sum - boson_product, matrix_sum_expected - boson_product_expected, 3); + checkSumEquals(boson_sum - matrix_product, boson_sum_expected - matrix_product_expected, 3); + checkSumEquals(spin_sum - boson_product, spin_sum_expected - boson_product_expected, 3); + checkSumEquals(boson_sum - spin_product, boson_sum_expected - spin_product_expected, 3); + } + + // `product - sum` + { + checkSumEquals(matrix_product - matrix_sum, matrix_product_expected - matrix_sum_expected, 3); + checkSumEquals(spin_product - spin_sum, spin_product_expected - spin_sum_expected, 3); + checkSumEquals(boson_product - boson_sum, boson_product_expected - boson_sum_expected, 3); + checkSumEquals(matrix_product - spin_sum, matrix_product_expected - spin_sum_expected, 3); + checkSumEquals(spin_product - matrix_sum, spin_product_expected - matrix_sum_expected, 3); + checkSumEquals(matrix_product - boson_sum, matrix_product_expected - boson_sum_expected, 3); + checkSumEquals(boson_product - matrix_sum, boson_product_expected - matrix_sum_expected, 3); + checkSumEquals(spin_product - boson_sum, spin_product_expected - boson_sum_expected, 3); + checkSumEquals(boson_product - spin_sum, boson_product_expected - spin_sum_expected, 3); + } + + // `sum - sum` + { + checkSumEquals(matrix_sum - matrix_sum, matrix_sum_expected - matrix_sum_expected); + checkSumEquals(spin_sum - spin_sum, spin_sum_expected - spin_sum_expected); + checkSumEquals(boson_sum - boson_sum, boson_sum_expected - boson_sum_expected); + checkSumEquals(matrix_sum - spin_sum, matrix_sum_expected - spin_sum_expected); + checkSumEquals(spin_sum - matrix_sum, spin_sum_expected - matrix_sum_expected); + checkSumEquals(matrix_sum - boson_sum, matrix_sum_expected - boson_sum_expected); + checkSumEquals(boson_sum - matrix_sum, boson_sum_expected - matrix_sum_expected); + checkSumEquals(spin_sum - boson_sum, spin_sum_expected - boson_sum_expected); + checkSumEquals(boson_sum - spin_sum, boson_sum_expected - spin_sum_expected); + } + + // `sum * product` + { + checkSumEquals(matrix_sum * matrix_product, matrix_sum_expected * matrix_product_expected, 2); + checkSumEquals(spin_sum * spin_product, spin_sum_expected * spin_product_expected, 2); + checkSumEquals(boson_sum * boson_product, boson_sum_expected * boson_product_expected, 2); + checkSumEquals(matrix_sum * spin_product, matrix_sum_expected * spin_product_expected, 2); + checkSumEquals(spin_sum * matrix_product, spin_sum_expected * matrix_product_expected, 2); + checkSumEquals(matrix_sum * boson_product, matrix_sum_expected * boson_product_expected, 2); + checkSumEquals(boson_sum * matrix_product, boson_sum_expected * matrix_product_expected, 2); + checkSumEquals(spin_sum * boson_product, spin_sum_expected * boson_product_expected, 2); + checkSumEquals(boson_sum * spin_product, boson_sum_expected * spin_product_expected, 2); + } + + // `product * sum` + { + checkSumEquals(matrix_product * matrix_sum, matrix_product_expected * matrix_sum_expected, 2); + checkSumEquals(spin_product * spin_sum, spin_product_expected * spin_sum_expected, 2); + checkSumEquals(boson_product * boson_sum, boson_product_expected * boson_sum_expected, 2); + checkSumEquals(matrix_product * spin_sum, matrix_product_expected * spin_sum_expected, 2); + checkSumEquals(spin_product * matrix_sum, spin_product_expected * matrix_sum_expected, 2); + checkSumEquals(matrix_product * boson_sum, matrix_product_expected * boson_sum_expected, 2); + checkSumEquals(boson_product * matrix_sum, boson_product_expected * matrix_sum_expected, 2); + checkSumEquals(spin_product * boson_sum, spin_product_expected * boson_sum_expected, 2); + checkSumEquals(boson_product * spin_sum, boson_product_expected * spin_sum_expected, 2); + } + + // `sum * sum` + { + checkSumEquals(matrix_sum * matrix_sum, matrix_sum_expected * matrix_sum_expected); + checkSumEquals(spin_sum * spin_sum, spin_sum_expected * spin_sum_expected); + checkSumEquals(boson_sum * boson_sum, boson_sum_expected * boson_sum_expected); + checkSumEquals(matrix_sum * spin_sum, matrix_sum_expected * spin_sum_expected); + checkSumEquals(spin_sum * matrix_sum, spin_sum_expected * matrix_sum_expected); + checkSumEquals(matrix_sum * boson_sum, matrix_sum_expected * boson_sum_expected); + checkSumEquals(boson_sum * matrix_sum, boson_sum_expected * matrix_sum_expected); + checkSumEquals(spin_sum * boson_sum, spin_sum_expected * boson_sum_expected); + checkSumEquals(boson_sum * spin_sum, boson_sum_expected * spin_sum_expected); + } + + // `sum += product` + { + auto matrix_sum_0 = matrix_sum; + matrix_sum_0 += matrix_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected + matrix_product_expected, 3); + + auto spin_sum_0 = spin_sum; + spin_sum_0 += spin_product; + checkSumEquals(spin_sum_0, spin_sum_expected + spin_product_expected, 3); + + auto boson_sum_0 = boson_sum; + boson_sum_0 += boson_product; + checkSumEquals(boson_sum_0, boson_sum_expected + boson_product_expected, 3); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 += spin_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected + spin_product_expected, 3); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 += boson_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected + boson_product_expected, 3); + } + + // `sum += sum` + { + auto matrix_sum_0 = matrix_sum; + matrix_sum_0 += matrix_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected + matrix_sum_expected); + + auto spin_sum_0 = spin_sum; + spin_sum_0 += spin_sum; + checkSumEquals(spin_sum_0, spin_sum_expected + spin_sum_expected); + + auto boson_sum_0 = boson_sum; + boson_sum_0 += boson_sum; + checkSumEquals(boson_sum_0, boson_sum_expected + boson_sum_expected); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 += spin_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected + spin_sum_expected); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 += boson_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected + boson_sum_expected); + } + + // `sum -= product` + { + auto matrix_sum_0 = matrix_sum; + matrix_sum_0 -= matrix_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected - matrix_product_expected, 3); + + auto spin_sum_0 = spin_sum; + spin_sum_0 -= spin_product; + checkSumEquals(spin_sum_0, spin_sum_expected - spin_product_expected, 3); + + auto boson_sum_0 = boson_sum; + boson_sum_0 -= boson_product; + checkSumEquals(boson_sum_0, boson_sum_expected - boson_product_expected, 3); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 -= spin_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected - spin_product_expected, 3); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 -= boson_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected - boson_product_expected, 3); + } + + // `sum -= sum` + { + auto matrix_sum_0 = matrix_sum; + matrix_sum_0 -= matrix_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected - matrix_sum_expected); + + auto spin_sum_0 = spin_sum; + spin_sum_0 -= spin_sum; + checkSumEquals(spin_sum_0, spin_sum_expected - spin_sum_expected); + + auto boson_sum_0 = boson_sum; + boson_sum_0 -= boson_sum; + checkSumEquals(boson_sum_0, boson_sum_expected - boson_sum_expected); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 -= spin_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected - spin_sum_expected); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 -= boson_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected - boson_sum_expected); + } + + // `sum *= product` + { + auto matrix_sum_0 = matrix_sum; + matrix_sum_0 *= matrix_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected * matrix_product_expected, 2); + + auto spin_sum_0 = spin_sum; + spin_sum_0 *= spin_product; + checkSumEquals(spin_sum_0, spin_sum_expected * spin_product_expected, 2); + + auto boson_sum_0 = boson_sum; + boson_sum_0 *= boson_product; + checkSumEquals(boson_sum_0, boson_sum_expected * boson_product_expected, 2); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 *= spin_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected * spin_product_expected, 2); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 *= boson_product; + checkSumEquals(matrix_sum_0, matrix_sum_expected * boson_product_expected, 2); + } + + // `sum *= sum` + { + auto matrix_sum_0 = matrix_sum; + matrix_sum_0 *= matrix_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected * matrix_sum_expected); + + auto spin_sum_0 = spin_sum; + spin_sum_0 *= spin_sum; + checkSumEquals(spin_sum_0, spin_sum_expected * spin_sum_expected); + + auto boson_sum_0 = boson_sum; + boson_sum_0 *= boson_sum; + checkSumEquals(boson_sum_0, boson_sum_expected * boson_sum_expected); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 *= spin_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected * spin_sum_expected); + + matrix_sum_0 = matrix_sum; + matrix_sum_0 *= boson_sum; + checkSumEquals(matrix_sum_0, matrix_sum_expected * boson_sum_expected); + } +} diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index 26467dc9ec..4b950bdf4e 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -786,172 +786,6 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { } } -// FIXME: add tests to combine general products with spin ops -TEST(OperatorExpressions, checkProductOperatorAgainstElementary) { - - int level_count = 2; - - // `product_operator + matrix_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); - - auto sum = product + elementary; - auto reverse = elementary + product; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix + elementary_matrix; - auto want_matrix_reverse = elementary_matrix + product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator + spin_operator` - { - auto mp = cudaq::matrix_operator::annihilate(0); - auto me = mp.get_terms()[0]; - auto sp = cudaq::spin_operator::x(1) * cudaq::spin_operator::x(1); - //auto fp = cudaq::fermion_operator::? - //auto s = me + sp; - auto s = mp + sp; - //auto r = sp + me; - auto r = sp + mp; - // auto r = fp + sp; auto r = sp + fp; - - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::spin_operator::x(1); - - auto sum = product + elementary; - auto reverse = elementary + product; - - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::PauliX_matrix(), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix + elementary_matrix; - auto want_matrix_reverse = elementary_matrix + product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator - matrix_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); - - auto difference = product - elementary; - auto reverse = elementary - product; - - ASSERT_TRUE(difference.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); - - auto got_matrix = - difference.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix - elementary_matrix; - auto want_matrix_reverse = elementary_matrix - product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator * matrix_operator` - { - auto term_0 = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); - - auto product = term_0 * elementary; - auto reverse = elementary * term_0; - - ASSERT_TRUE(product.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); - - auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix * elementary_matrix; - auto want_matrix_reverse = elementary_matrix * product_matrix; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `product_operator *= matrix_operator` - { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto elementary = cudaq::matrix_operator::create(1); - - product *= elementary; - - ASSERT_TRUE(product.n_terms() == 3); - - auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); - - auto product_matrix = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)) * - cudaq::kronecker(utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)); - auto elementary_matrix = cudaq::kronecker( - utils::create_matrix(level_count), utils::id_matrix(level_count)); - - auto want_matrix = product_matrix * elementary_matrix; - - utils::checkEqual(want_matrix, got_matrix); - } -} - -// FIXME: add tests to combine general product with spin product TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; From 261f74464b4a1044134a679bf6f6bb23e1480cf0 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 10 Feb 2025 17:42:43 +0000 Subject: [PATCH 263/311] gathering some perf numbers before optimization Signed-off-by: Bettina Heim --- cmake/Modules/CMakeLists.txt | 1 + cmake/Modules/CUDAQConfig.cmake | 3 + cmake/Modules/CUDAQEmDefaultConfig.cmake | 3 + cmake/Modules/CUDAQOperatorConfig.cmake | 13 ++ .../Modules/CUDAQPlatformDefaultConfig.cmake | 3 + cmake/Modules/NVQIRConfig.cmake.in | 1 + main.cpp | 185 ++++++++++++++++++ runtime/cudaq/CMakeLists.txt | 4 +- runtime/cudaq/dynamics/CMakeLists.txt | 6 +- scripts/build_cudaq.sh | 2 +- unittests/CMakeLists.txt | 2 +- 11 files changed, 216 insertions(+), 7 deletions(-) create mode 100644 cmake/Modules/CUDAQOperatorConfig.cmake create mode 100644 main.cpp diff --git a/cmake/Modules/CMakeLists.txt b/cmake/Modules/CMakeLists.txt index b894b3cc26..8c26addf34 100644 --- a/cmake/Modules/CMakeLists.txt +++ b/cmake/Modules/CMakeLists.txt @@ -11,6 +11,7 @@ set(CONFIG_FILES CUDAQEmDefaultConfig.cmake CUDAQNloptConfig.cmake CUDAQSpinConfig.cmake + CUDAQOperatorConfig.cmake CUDAQConfig.cmake CUDAQEnsmallenConfig.cmake CUDAQPlatformDefaultConfig.cmake diff --git a/cmake/Modules/CUDAQConfig.cmake b/cmake/Modules/CUDAQConfig.cmake index d27e9d57ac..1d29e8bef5 100644 --- a/cmake/Modules/CUDAQConfig.cmake +++ b/cmake/Modules/CUDAQConfig.cmake @@ -14,6 +14,9 @@ list(APPEND CMAKE_MODULE_PATH "${CUDAQ_CMAKE_DIR}") set (CUDAQSpin_DIR "${CUDAQ_CMAKE_DIR}") find_dependency(CUDAQSpin REQUIRED) +set (CUDAQOperator_DIR "${CUDAQ_CMAKE_DIR}") +find_dependency(CUDAQOperator REQUIRED) + set (CUDAQCommon_DIR "${CUDAQ_CMAKE_DIR}") find_dependency(CUDAQCommon REQUIRED) diff --git a/cmake/Modules/CUDAQEmDefaultConfig.cmake b/cmake/Modules/CUDAQEmDefaultConfig.cmake index 663334cf3d..a591cd002d 100644 --- a/cmake/Modules/CUDAQEmDefaultConfig.cmake +++ b/cmake/Modules/CUDAQEmDefaultConfig.cmake @@ -11,6 +11,9 @@ get_filename_component(CUDAQ_EM_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) set (CUDAQSpin_DIR "${CUDAQ_EM_CMAKE_DIR}") find_dependency(CUDAQSpin REQUIRED) +set (CUDAQOperator_DIR "${CUDAQ_EM_CMAKE_DIR}") +find_dependency(CUDAQOperator REQUIRED) + set (CUDAQCommon_DIR "${CUDAQ_EM_CMAKE_DIR}") find_dependency(CUDAQCommon REQUIRED) diff --git a/cmake/Modules/CUDAQOperatorConfig.cmake b/cmake/Modules/CUDAQOperatorConfig.cmake new file mode 100644 index 0000000000..c54ec8e1fe --- /dev/null +++ b/cmake/Modules/CUDAQOperatorConfig.cmake @@ -0,0 +1,13 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +get_filename_component(CUDAQ_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +if(NOT TARGET cudaq::cudaq-operator) + include("${CUDAQ_CMAKE_DIR}/CUDAQOperatorTargets.cmake") +endif() diff --git a/cmake/Modules/CUDAQPlatformDefaultConfig.cmake b/cmake/Modules/CUDAQPlatformDefaultConfig.cmake index 213804e52a..7731250fac 100644 --- a/cmake/Modules/CUDAQPlatformDefaultConfig.cmake +++ b/cmake/Modules/CUDAQPlatformDefaultConfig.cmake @@ -11,6 +11,9 @@ get_filename_component(CUDAQ_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) set (CUDAQEmDefault_DIR "${CUDAQ_CMAKE_DIR}") find_dependency(CUDAQEmDefault REQUIRED) +set (CUDAQOperator_DIR "${CUDAQ_CMAKE_DIR}") +find_dependency(CUDAQOperator REQUIRED) + set (CUDAQSpin_DIR "${CUDAQ_CMAKE_DIR}") find_dependency(CUDAQSpin REQUIRED) diff --git a/cmake/Modules/NVQIRConfig.cmake.in b/cmake/Modules/NVQIRConfig.cmake.in index 0406c339ea..17cd691cbc 100644 --- a/cmake/Modules/NVQIRConfig.cmake.in +++ b/cmake/Modules/NVQIRConfig.cmake.in @@ -12,6 +12,7 @@ include(CMakeFindDependencyMacro) get_filename_component(PARENT_DIRECTORY ${NVQIR_CMAKE_DIR} DIRECTORY) find_dependency(CUDAQSpin REQUIRED HINTS "${PARENT_DIRECTORY}/cudaq") +find_dependency(CUDAQOperator REQUIRED HINTS "${PARENT_DIRECTORY}/cudaq") find_dependency(CUDAQCommon REQUIRED HINTS "${PARENT_DIRECTORY}/cudaq") find_package(fmt QUIET) if (NOT fmt_FOUND) diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000000..624d93f25d --- /dev/null +++ b/main.cpp @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include +#include + +int main() { + + // multiplication inplace with itself + + cudaq::spin_op spin_op = cudaq::spin::i(0); + cudaq::product_operator prod_op = cudaq::spin_operator::i(0); + + int nr_reps = 1000; + std::cout << "multiplication inplace with itself" << std::endl; + + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(0); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // multiplication inplace with other + + spin_op = cudaq::spin::i(0); + prod_op = cudaq::spin_operator::i(0); + + nr_reps = 1000; + std::cout << "multiplication inplace with other" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with itself + + spin_op = cudaq::spin::i(0); + // auto op_sum = cudaq::operator_sum(cudaq::spin_operator::i(0)); // fixme: protected + // cudaq::operator_sum op_sum = cudaq::spin_operator::i(0); // fixme: protected + prod_op = cudaq::spin_operator::i(0); + cudaq::operator_sum op_sum = prod_op; + + nr_reps = 1000; + std::cout << "addition inplace with itself" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(0); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(0); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with other + + spin_op = cudaq::spin::i(0); + prod_op = cudaq::spin_operator::i(0); + op_sum = prod_op; + + nr_reps = 1000; + std::cout << "addition inplace with other" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + + // addition inplace with product self + + auto spin_prod = cudaq::spin::i(0); + for (auto i = 0; i < 100; ++i) + spin_prod *= cudaq::spin::x(i); + spin_op = spin_prod; + prod_op = cudaq::spin_operator::i(0); + for (auto i = 0; i < 100; ++i) + prod_op *= cudaq::spin_operator::x(i); + op_sum = prod_op; + + nr_reps = 1000; + std::cout << "addition inplace with product self" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // product inplace with 2-term sum on fixed degrees + + auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); + spin_op = spin_term; + auto prod_term = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(1); + op_sum = prod_term; + + nr_reps = 20; + std::cout << "product inplace with 2-term sum on fixed degrees" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= spin_term; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= prod_term; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // product inplace with 2-term sum on varying degrees + + spin_op = cudaq::spin::i(0); + prod_op = cudaq::spin_operator::i(0); + op_sum = prod_op; + + nr_reps = 20; + std::cout << "product inplace with 2-term sum on varying degrees" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(i) + cudaq::spin::z(i + 1); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + return 0; +} \ No newline at end of file diff --git a/runtime/cudaq/CMakeLists.txt b/runtime/cudaq/CMakeLists.txt index d2f23bd0f9..d4115de94b 100644 --- a/runtime/cudaq/CMakeLists.txt +++ b/runtime/cudaq/CMakeLists.txt @@ -40,7 +40,7 @@ if (CUDA_FOUND) PRIVATE .) target_link_libraries(${LIBRARY_NAME} - PUBLIC dl cudaq-spin cudaq-operators cudaq-common cudaq-nlopt cudaq-ensmallen + PUBLIC dl cudaq-spin cudaq-operator cudaq-common cudaq-nlopt cudaq-ensmallen PRIVATE nvqir fmt::fmt-header-only CUDA::cudart_static) target_compile_definitions(${LIBRARY_NAME} PRIVATE CUDAQ_HAS_CUDA) @@ -52,7 +52,7 @@ else() PRIVATE .) target_link_libraries(${LIBRARY_NAME} - PUBLIC dl cudaq-spin cudaq-operators cudaq-common cudaq-nlopt cudaq-ensmallen + PUBLIC dl cudaq-spin cudaq-operator cudaq-common cudaq-nlopt cudaq-ensmallen PRIVATE nvqir fmt::fmt-header-only) endif() diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index cbaa3d544c..15cea1ab27 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -6,8 +6,8 @@ # the terms of the Apache License 2.0 which accompanies this distribution. # # ============================================================================ # -set(LIBRARY_NAME cudaq-operators) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") +set(LIBRARY_NAME cudaq-operator) +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC @@ -45,6 +45,6 @@ target_link_libraries(${LIBRARY_NAME} PRIVATE ${OPERATOR_DEPENDENCIES}) install(TARGETS ${LIBRARY_NAME} EXPORT cudaq-operator-targets DESTINATION lib) install(EXPORT cudaq-operator-targets - FILE CUDAQSpinTargets.cmake + FILE CUDAQOperatorTargets.cmake NAMESPACE cudaq:: DESTINATION lib/cmake/cudaq) diff --git a/scripts/build_cudaq.sh b/scripts/build_cudaq.sh index 1384620bcc..95cbf387e0 100644 --- a/scripts/build_cudaq.sh +++ b/scripts/build_cudaq.sh @@ -73,7 +73,7 @@ repo_root=$(cd "$this_file_dir" && git rev-parse --show-toplevel) # Prepare the build directory mkdir -p "$CUDAQ_INSTALL_PREFIX/bin" -mkdir -p "$working_dir/build" && cd "$working_dir/build" && rm -rf * +mkdir -p "$working_dir/build" && cd "$working_dir/build" # && rm -rf * mkdir -p logs && rm -rf logs/* if [ -n "$install_toolchain" ]; then diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 46f4c14d2e..294968aead 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -279,7 +279,7 @@ endif() target_link_libraries(test_operators PRIVATE cudaq-spin - cudaq-operators + cudaq-operator cudaq gtest_main fmt::fmt-header-only) From 705a650c9abbcf3a2a613baa4c634d3102a295d2 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 10 Feb 2025 17:43:11 +0000 Subject: [PATCH 264/311] always link the operator lib Signed-off-by: Bettina Heim --- tools/nvqpp/nvq++.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/nvqpp/nvq++.in b/tools/nvqpp/nvq++.in index ae0aabdd3a..ee62e38388 100644 --- a/tools/nvqpp/nvq++.in +++ b/tools/nvqpp/nvq++.in @@ -303,7 +303,7 @@ LIBRARY_MODE_EXECUTION_MANAGER="default" PLATFORM_LIBRARY="default" LLVM_QUANTUM_TARGET="qir" LINKDIRS="-L${install_dir}/lib -L${install_dir}/lib/plugins @CUDAQ_CXX_NVQPP_LINK_STR@" -LINKLIBS="-lcudaq -lcudaq-common -lcudaq-ensmallen -lcudaq-nlopt -lcudaq-spin" +LINKLIBS="-lcudaq -lcudaq-common -lcudaq-ensmallen -lcudaq-nlopt -lcudaq-spin -lcudaq-operator" # Add any plugin libraries to the link stage CUDAQ_PLUGIN_DIR=${install_dir}/lib/plugins From b422030ccac3e87dcf06835598d44aaf61ab309e Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 10 Feb 2025 17:44:03 +0000 Subject: [PATCH 265/311] undoing accidentally committed change Signed-off-by: Bettina Heim --- scripts/build_cudaq.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/build_cudaq.sh b/scripts/build_cudaq.sh index 95cbf387e0..1384620bcc 100644 --- a/scripts/build_cudaq.sh +++ b/scripts/build_cudaq.sh @@ -73,7 +73,7 @@ repo_root=$(cd "$this_file_dir" && git rev-parse --show-toplevel) # Prepare the build directory mkdir -p "$CUDAQ_INSTALL_PREFIX/bin" -mkdir -p "$working_dir/build" && cd "$working_dir/build" # && rm -rf * +mkdir -p "$working_dir/build" && cd "$working_dir/build" && rm -rf * mkdir -p logs && rm -rf logs/* if [ -n "$install_toolchain" ]; then From e411cb7c0c18e223df0ac07b0d8bf7ebd7f55ece Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 10 Feb 2025 22:24:17 +0000 Subject: [PATCH 266/311] product optimizations Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/operator_sum.cpp | 130 +++++++------ runtime/cudaq/dynamics/product_operators.cpp | 92 ++++++---- runtime/cudaq/dynamics/spin_operators.cpp | 12 ++ runtime/cudaq/dynamics/spin_operators.h | 5 + runtime/cudaq/dynamics/templates.h | 21 ++- runtime/cudaq/operators.h | 181 +++++++++---------- unittests/dynamics/matrix_operator.cpp | 52 +++--- unittests/dynamics/operator_conversions.cpp | 26 +-- unittests/dynamics/operator_sum.cpp | 120 ++++++------ unittests/dynamics/product_operator.cpp | 114 ++++++------ unittests/dynamics/spin_operator.cpp | 69 ++++--- 11 files changed, 422 insertions(+), 400 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 25c2c8ef9c..32e52529f0 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -112,9 +112,9 @@ std::vector operator_sum::degrees() const { return cudaq::detail::canonicalize_degrees(degrees); } -template -int operator_sum::n_terms() const { - return this->terms.size(); +template +int operator_sum::num_terms() const { + return this->terms.size(); } template @@ -134,7 +134,7 @@ operator_sum::get_terms() const { std::vector operator_sum::degrees() const; \ \ template \ - int operator_sum::n_terms() const; \ + int operator_sum::num_terms() const; \ \ template \ std::vector> operator_sum::get_terms() const; @@ -408,6 +408,7 @@ SUM_ADDITION(std::complex, -); SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); +/* template operator_sum operator_sum::operator*(const HandlerTy &other) const { @@ -451,6 +452,7 @@ operator_sum::operator*(const HandlerTy &other) const { SUM_ADDITION_HANDLER(+) SUM_ADDITION_HANDLER(-) +*/ #define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ \ @@ -472,38 +474,31 @@ SUM_ADDITION_HANDLER(-) operator_sum operator_sum::operator+(const scalar_operator &other) const; \ template \ operator_sum operator_sum::operator-(const scalar_operator &other) const; \ + +/* template \ operator_sum operator_sum::operator*(const HandlerTy &other) const; \ template \ operator_sum operator_sum::operator+(const HandlerTy &other) const; \ template \ operator_sum operator_sum::operator-(const HandlerTy &other) const; +*/ INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template -operator_sum operator_sum::operator*( - const product_operator &other) const { - std::vector coefficients; - coefficients.reserve(this->coefficients.size()); - for (auto &coeff : this->coefficients) - coefficients.push_back(other.coefficient * coeff); - std::vector> terms; - terms.reserve(this->terms.size()); - for (auto &term : this->terms) { - std::vector prod; - prod.reserve(term.size() + other.operators.size()); - for (auto &op : term) - prod.push_back(op); - for (auto &op : other.operators) - prod.push_back(op); - terms.push_back(std::move(prod)); - } +operator_sum operator_sum::operator*(const product_operator &other) const { operator_sum sum; - sum.coefficients = std::move(coefficients); - sum.terms = std::move(terms); + sum.coefficients.reserve(this->coefficients.size()); + sum.terms.reserve(this->terms.size()); + for (auto i = 0; i < this->terms.size(); ++i) { + product_operator prod(this->coefficients[i], this->terms[i]); + prod *= other; + sum.coefficients.push_back(std::move(prod.coefficient)); + sum.terms.push_back(std::move(prod.operators)); + } return sum; } @@ -531,30 +526,18 @@ SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum -operator_sum::operator*(const operator_sum &other) const { - std::vector coefficients; - coefficients.reserve(this->coefficients.size() * other.coefficients.size()); - for (auto &coeff1 : this->coefficients) { - for (auto &coeff2 : other.coefficients) - coefficients.push_back(coeff1 * coeff2); - } - std::vector> terms; - terms.reserve(this->terms.size() * other.terms.size()); - for (auto &term1 : this->terms) { - for (auto &term2 : other.terms) { - std::vector prod; - prod.reserve(term1.size() + term2.size()); - for (auto &op : term1) - prod.push_back(op); - for (auto &op : term2) - prod.push_back(op); - terms.push_back(std::move(prod)); +operator_sum operator_sum::operator*(const operator_sum &other) const { + operator_sum sum; + sum.coefficients.reserve(this->coefficients.size() * other.coefficients.size()); + sum.terms.reserve(this->terms.size() * other.terms.size()); + for (auto i = 0; i < this->terms.size(); ++i) { + for (auto j = 0; j < other.terms.size(); ++j) { + product_operator prod(this->coefficients[i], this->terms[i]); + prod *= product_operator(other.coefficients[j], other.terms[j]); + sum.coefficients.push_back(std::move(prod.coefficient)); + sum.terms.push_back(std::move(prod.operators)); } } - operator_sum sum; - sum.coefficients = std::move(coefficients); - sum.terms = std::move(terms); return sum; } @@ -638,6 +621,7 @@ SUM_ADDITION_ASSIGNMENT(std::complex, -); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); +/* template operator_sum & operator_sum::operator*=(const HandlerTy &other) { @@ -660,16 +644,15 @@ operator_sum::operator*=(const HandlerTy &other) { SUM_ADDITION_HANDLER_ASSIGNMENT(+) SUM_ADDITION_HANDLER_ASSIGNMENT(-) +*/ template -operator_sum & -operator_sum::operator*=(const product_operator &other) { - for (auto &coeff : this->coefficients) - coeff *= other.coefficient; - for (auto &term : this->terms) { - term.reserve(term.size() + other.operators.size()); - for (auto &op : other.operators) - term.push_back(op); +operator_sum& operator_sum::operator*=(const product_operator &other) { + for (auto i = 0; i < this->terms.size(); ++i) { + product_operator prod(this->coefficients[i], this->terms[i]); + prod *= other; + this->coefficients[i] = std::move(prod.coefficient); + this->terms[i] = std::move(prod.operators); } return *this; } @@ -732,12 +715,6 @@ SUM_ADDITION_SUM_ASSIGNMENT(-); template \ operator_sum& operator_sum::operator-=(const scalar_operator &other); \ template \ - operator_sum& operator_sum::operator*=(const HandlerTy &other); \ - template \ - operator_sum& operator_sum::operator+=(const HandlerTy &other); \ - template \ - operator_sum& operator_sum::operator-=(const HandlerTy &other); \ - template \ operator_sum& operator_sum::operator*=(const product_operator &other); \ template \ operator_sum& operator_sum::operator+=(const product_operator &other); \ @@ -750,6 +727,16 @@ SUM_ADDITION_SUM_ASSIGNMENT(-); template \ operator_sum& operator_sum::operator+=(const operator_sum &other); +/* + template \ + operator_sum& operator_sum::operator*=(const HandlerTy &other); \ + template \ + operator_sum& operator_sum::operator+=(const HandlerTy &other); \ + template \ + operator_sum& operator_sum::operator-=(const HandlerTy &other); \ + +*/ + INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); @@ -801,6 +788,7 @@ SUM_ADDITION_REVERSE(std::complex, -); SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); +/* template operator_sum operator*(const HandlerTy &other, const operator_sum &self) { @@ -844,33 +832,37 @@ operator_sum operator*(const HandlerTy &other, SUM_ADDITION_HANDLER_REVERSE(+) SUM_ADDITION_HANDLER_REVERSE(-) +*/ #define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ \ template \ - operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ - template \ - operator_sum operator*(std::complex other, const operator_sum &self); \ - template \ operator_sum operator*(double other, const operator_sum &self); \ template \ - operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ + operator_sum operator+(double other, const operator_sum &self); \ template \ - operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ + operator_sum operator-(double other, const operator_sum &self); \ template \ - operator_sum operator+(double other, const operator_sum &self); \ + operator_sum operator*(std::complex other, const operator_sum &self); \ template \ operator_sum operator+(std::complex other, const operator_sum &self); \ template \ - operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ + operator_sum operator-(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ template \ operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ + +/* template \ - operator_sum operator-(double other, const operator_sum &self); \ + operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ template \ - operator_sum operator-(std::complex other, const operator_sum &self); \ + operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ template \ operator_sum operator-(const HandlerTy &other, const operator_sum &self); +*/ INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index e93c017328..a9dfdd90d9 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -117,7 +117,7 @@ std::vector product_operator::degrees() const { } template -int product_operator::n_terms() const { +int product_operator::num_terms() const { return this->operators.size(); } @@ -137,7 +137,7 @@ scalar_operator product_operator::get_coefficient() const { std::vector product_operator::degrees() const; \ \ template \ - int product_operator::n_terms() const; \ + int product_operator::num_terms() const; \ \ template \ const std::vector& product_operator::get_terms() const; \ @@ -389,6 +389,7 @@ PRODUCT_ADDITION(std::complex, -); PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); +/* template product_operator product_operator::operator*(const HandlerTy &other) const { @@ -410,6 +411,7 @@ product_operator::operator*(const HandlerTy &other) const { PRODUCT_ADDITION_HANDLER(+) PRODUCT_ADDITION_HANDLER(-) +*/ #define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ \ @@ -431,12 +433,15 @@ PRODUCT_ADDITION_HANDLER(-) operator_sum product_operator::operator+(const scalar_operator &other) const; \ template \ operator_sum product_operator::operator-(const scalar_operator &other) const; \ + +/* template \ product_operator product_operator::operator*(const HandlerTy &other) const; \ template \ operator_sum product_operator::operator+(const HandlerTy &other) const; \ template \ - operator_sum product_operator::operator-(const HandlerTy &other) const; + operator_sum product_operator::operator-(const HandlerTy &other) const; +*/ INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); @@ -449,9 +454,7 @@ product_operator product_operator::operator*( terms.reserve(this->operators.size() + other.operators.size()); for (auto &term : this->operators) terms.push_back(term); - for (auto &term : other.operators) - terms.push_back(term); - return product_operator(this->coefficient * other.coefficient, std::move(terms)); + return product_operator(this->coefficient, std::move(terms)) *= other; } #define PRODUCT_ADDITION_PRODUCT(op) \ @@ -465,26 +468,15 @@ PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) template -operator_sum product_operator::operator*( - const operator_sum &other) const { - std::vector coefficients; - coefficients.reserve(other.coefficients.size()); - for (auto &coeff : other.coefficients) - coefficients.push_back(this->coefficient * coeff); - std::vector> terms; - terms.reserve(other.terms.size()); - for (auto &term : other.terms) { - std::vector prod; - prod.reserve(this->operators.size() + term.size()); - for (auto &op : this->operators) - prod.push_back(op); - for (auto &op : term) - prod.push_back(op); - terms.push_back(std::move(prod)); - } +operator_sum product_operator::operator*(const operator_sum &other) const { operator_sum sum; - sum.coefficients = std::move(coefficients); - sum.terms = std::move(terms); + sum.coefficients.reserve(other.coefficients.size()); + sum.terms.reserve(other.terms.size()); + for (auto i = 0; i < other.terms.size(); ++i) { + auto prod = *this * product_operator(other.coefficients[i], other.terms[i]); + sum.coefficients.push_back(std::move(prod.coefficient)); + sum.terms.push_back(std::move(prod.operators)); + } return sum; } @@ -547,11 +539,13 @@ PRODUCT_MULTIPLICATION_ASSIGNMENT(double); PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); +/* template product_operator& product_operator::operator*=(const HandlerTy &other) { this->operators.push_back(other); return *this; } +*/ template product_operator& product_operator::operator*=(const product_operator &other) { @@ -561,6 +555,22 @@ product_operator& product_operator::operator*=(const produ return *this; } +template <> // specialization for term aggregation +product_operator& product_operator::operator*=(const product_operator &other) { + this->operators.reserve(this->operators.size() + other.operators.size()); // worst case, may not be needed + std::complex spin_factor = 1.0; + for (const spin_operator &op : other.operators) { + auto it = std::find_if(this->operators.rbegin(), this->operators.rend(), + [op_target = op.target] (const spin_operator& self_op) { return self_op.target == op_target; }); + if (it != this->operators.rend()) + spin_factor *= it->in_place_mult(op); + else + this->operators.push_back(op); + } + this->coefficient *= other.coefficient * spin_factor; + return *this; +} + #define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ \ template \ @@ -570,10 +580,13 @@ product_operator& product_operator::operator*=(const produ template \ product_operator& product_operator::operator*=(const scalar_operator &other); \ template \ - product_operator& product_operator::operator*=(const HandlerTy &other); \ - template \ product_operator& product_operator::operator*=(const product_operator &other); +/* + template \ + product_operator& product_operator::operator*=(const HandlerTy &other); \ +*/ + INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); @@ -606,6 +619,7 @@ PRODUCT_ADDITION_REVERSE(std::complex, -); PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); +/* template product_operator operator*(const HandlerTy &other, const product_operator &self) { @@ -626,33 +640,37 @@ product_operator operator*(const HandlerTy &other, PRODUCT_ADDITION_HANDLER_REVERSE(+) PRODUCT_ADDITION_HANDLER_REVERSE(-) +*/ #define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ \ template \ product_operator operator*(double other, const product_operator &self); \ template \ - product_operator operator*(std::complex other, const product_operator &self); \ - template \ - product_operator operator*(const scalar_operator &other, const product_operator &self); \ + operator_sum operator+(double other, const product_operator &self); \ template \ - product_operator operator*(const HandlerTy &other, const product_operator &self); \ + operator_sum operator-(double other, const product_operator &self); \ template \ - operator_sum operator+(double other, const product_operator &self); \ + product_operator operator*(std::complex other, const product_operator &self); \ template \ operator_sum operator+(std::complex other, const product_operator &self); \ template \ - operator_sum operator+(const scalar_operator &other, const product_operator &self); \ + operator_sum operator-(std::complex other, const product_operator &self); \ template \ - operator_sum operator+(const HandlerTy &other, const product_operator &self); \ + product_operator operator*(const scalar_operator &other, const product_operator &self); \ template \ - operator_sum operator-(double other, const product_operator &self); \ + operator_sum operator+(const scalar_operator &other, const product_operator &self); \ template \ - operator_sum operator-(std::complex other, const product_operator &self); \ + operator_sum operator-(const scalar_operator &other, const product_operator &self); + +/* template \ - operator_sum operator-(const scalar_operator &other, const product_operator &self); \ + product_operator operator*(const HandlerTy &other, const product_operator &self); \ + template \ + operator_sum operator+(const HandlerTy &other, const product_operator &self); \ template \ operator_sum operator-(const HandlerTy &other, const product_operator &self); +*/ INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index d2859314eb..6f9668b697 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -15,6 +15,18 @@ namespace cudaq { +// private helper to optimize arithmetics + +std::complex spin_operator::in_place_mult(const spin_operator &other) { + assert(this->target == other.target); // FIXME: make cleaner + std::complex factor; + if (this->id == 0 || other.id == 0 || this->id == other.id) factor = 1.0; + else if (this->id + 1 == other.id || this->id - 2 == other.id) factor = 1.0j; + else factor = -1.0j; + this->id ^= other.id; + return factor; +} + // read-only properties std::vector spin_operator::degrees() const { diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 043d210473..491706a769 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -22,6 +22,7 @@ class product_operator; // FIXME: rename to spin ... class spin_operator : operator_handler{ +friend class product_operator; private: @@ -31,6 +32,10 @@ class spin_operator : operator_handler{ spin_operator(int op, int target); + // private helper to optimize arithmetics + + std::complex in_place_mult(const spin_operator &other); + public: // read-only properties diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 43aab3c128..e238c9fa5d 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -46,12 +46,15 @@ template operator_sum operator+(const scalar_operator &other, const product_operator &self); template operator_sum operator-(const scalar_operator &other, const product_operator &self); + +/* template product_operator operator*(const HandlerTy &other, const product_operator &self); template operator_sum operator+(const HandlerTy &other, const product_operator &self); template operator_sum operator-(const HandlerTy &other, const product_operator &self); +*/ template product_operator operator*(const product_operator &other, const product_operator &self); @@ -78,12 +81,14 @@ template operator_sum operator+(const scalar_operator &other, const operator_sum &self); template operator_sum operator-(const scalar_operator &other, const operator_sum &self); +/* template operator_sum operator*(const HandlerTy &other, const operator_sum &self); template operator_sum operator+(const HandlerTy &other, const operator_sum &self); template operator_sum operator-(const HandlerTy &other, const operator_sum &self); +*/ template operator_sum operator*(const operator_sum &other, const product_operator &self); @@ -126,12 +131,6 @@ operator_sum operator-(const operator_sum &other, const operator_sum operator+(const scalar_operator &other, const product_operator &self); \ extern template \ operator_sum operator-(const scalar_operator &other, const product_operator &self); \ - extern template \ - product_operator operator*(const HandlerTy &other, const product_operator &self); \ - extern template \ - operator_sum operator+(const HandlerTy &other, const product_operator &self); \ - extern template \ - operator_sum operator-(const HandlerTy &other, const product_operator &self); \ \ extern template \ operator_sum operator*(double other, const operator_sum &self); \ @@ -151,12 +150,22 @@ operator_sum operator-(const operator_sum &other, const operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ extern template \ operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ + +/* + extern template \ + product_operator operator*(const HandlerTy &other, const product_operator &self); \ + extern template \ + operator_sum operator+(const HandlerTy &other, const product_operator &self); \ + extern template \ + operator_sum operator-(const HandlerTy &other, const product_operator &self); \ + extern template \ operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ extern template \ operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ extern template \ operator_sum operator-(const HandlerTy &other, const operator_sum &self); +*/ EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index bd3a108ebf..e1bd9d37d7 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -58,7 +58,7 @@ template friend class product_operator; std::vector degrees() const; /// @brief Return the number of operator terms that make up this operator sum. - int n_terms() const; + int num_terms() const; /// FIXME: GET RID OF THIS (MAKE ITERABLE INSTEAD) std::vector> get_terms() const; @@ -134,37 +134,38 @@ template friend class product_operator; operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; + /* operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; operator_sum operator*(const HandlerTy &other) const; - operator_sum - operator*(const product_operator &other) const; - operator_sum - operator+(const product_operator &other) const; - operator_sum - operator-(const product_operator &other) const; + */ + operator_sum operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; - operator_sum &operator*=(double other); - operator_sum &operator+=(double other); - operator_sum &operator-=(double other); - operator_sum &operator*=(std::complex other); - operator_sum &operator+=(std::complex other); - operator_sum &operator-=(std::complex other); - operator_sum &operator*=(const scalar_operator &other); - operator_sum &operator+=(const scalar_operator &other); - operator_sum &operator-=(const scalar_operator &other); - operator_sum &operator*=(const HandlerTy &other); - operator_sum &operator+=(const HandlerTy &other); - operator_sum &operator-=(const HandlerTy &other); - operator_sum &operator*=(const product_operator &other); - operator_sum &operator+=(const product_operator &other); - operator_sum &operator-=(const product_operator &other); - operator_sum &operator*=(const operator_sum &other); - operator_sum &operator+=(const operator_sum &other); - operator_sum &operator-=(const operator_sum &other); + operator_sum& operator*=(double other); + operator_sum& operator+=(double other); + operator_sum& operator-=(double other); + operator_sum& operator*=(std::complex other); + operator_sum& operator+=(std::complex other); + operator_sum& operator-=(std::complex other); + operator_sum& operator*=(const scalar_operator &other); + operator_sum& operator+=(const scalar_operator &other); + operator_sum& operator-=(const scalar_operator &other); + /* + operator_sum& operator*=(const HandlerTy &other); + operator_sum& operator+=(const HandlerTy &other); + operator_sum& operator-=(const HandlerTy &other); + */ + operator_sum& operator*=(const product_operator &other); + operator_sum& operator+=(const product_operator &other); + operator_sum& operator-=(const product_operator &other); + operator_sum& operator*=(const operator_sum &other); + operator_sum& operator+=(const operator_sum &other); + operator_sum& operator-=(const operator_sum &other); // left-hand arithmetics @@ -176,30 +177,26 @@ template friend class product_operator; friend operator_sum operator+(double other, const operator_sum &self); template friend operator_sum operator-(double other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, - const operator_sum &self); - template - friend operator_sum operator+(std::complex other, - const operator_sum &self); - template - friend operator_sum operator-(std::complex other, - const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, - const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, - const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, - const operator_sum &self); - template + template + friend operator_sum operator*(std::complex other, const operator_sum &self); + template + friend operator_sum operator+(std::complex other, const operator_sum &self); + template + friend operator_sum operator-(std::complex other, const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + /* + template friend operator_sum operator*(const T &other, const operator_sum &self); template friend operator_sum operator+(const T &other, const operator_sum &self); - template - friend operator_sum operator-(const T &other, const operator_sum &self); + template + friend operator_sum operator-(const T &other, const operator_sum &self); + */ template friend operator_sum operator+(double other, const product_operator &self); @@ -213,10 +210,13 @@ template friend class product_operator; friend operator_sum operator+(const scalar_operator &other, const product_operator &self); template friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + + /* template friend operator_sum operator+(const T &other, const product_operator &self); template friend operator_sum operator-(const T &other, const product_operator &self); + */ }; /// @brief Represents an operator expression consisting of a product of @@ -257,7 +257,7 @@ template friend class operator_sum; /// @brief Return the number of operator terms that make up this product /// operator. - int n_terms() const; + int num_terms() const; /// FIXME: GET RID OF THIS (MAKE ITERABLE INSTEAD) const std::vector& get_terms() const; @@ -337,66 +337,53 @@ template friend class operator_sum; product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; + /* product_operator operator*(const HandlerTy &other) const; operator_sum operator+(const HandlerTy &other) const; operator_sum operator-(const HandlerTy &other) const; - product_operator - operator*(const product_operator &other) const; - operator_sum - operator+(const product_operator &other) const; - operator_sum - operator-(const product_operator &other) const; + */ + product_operator operator*(const product_operator &other) const; + operator_sum operator+(const product_operator &other) const; + operator_sum operator-(const product_operator &other) const; operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - product_operator &operator*=(double other); - product_operator &operator*=(std::complex other); - product_operator &operator*=(const scalar_operator &other); - product_operator &operator*=(const HandlerTy &other); - product_operator & - operator*=(const product_operator &other); + product_operator& operator*=(double other); + product_operator& operator*=(std::complex other); + product_operator& operator*=(const scalar_operator &other); + // product_operator& operator*=(const HandlerTy &other); + product_operator& operator*=(const product_operator &other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template - // instantiation is a nightmare. - template - friend product_operator operator*(double other, - const product_operator &self); - template - friend operator_sum operator+(double other, - const product_operator &self); - template - friend operator_sum operator-(double other, - const product_operator &self); - template - friend product_operator operator*(std::complex other, - const product_operator &self); - template - friend operator_sum operator+(std::complex other, - const product_operator &self); - template - friend operator_sum operator-(std::complex other, - const product_operator &self); - template - friend product_operator operator*(const scalar_operator &other, - const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, - const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, - const product_operator &self); - template - friend product_operator operator*(const T &other, - const product_operator &self); - template - friend operator_sum operator+(const T &other, - const product_operator &self); - template - friend operator_sum operator-(const T &other, - const product_operator &self); + // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. + template + friend product_operator operator*(double other, const product_operator &self); + template + friend operator_sum operator+(double other, const product_operator &self); + template + friend operator_sum operator-(double other, const product_operator &self); + template + friend product_operator operator*(std::complex other, const product_operator &self); + template + friend operator_sum operator+(std::complex other, const product_operator &self); + template + friend operator_sum operator-(std::complex other, const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + /* + template + friend product_operator operator*(const T &other, const product_operator &self); + template + friend operator_sum operator+(const T &other, const product_operator &self); + template + friend operator_sum operator-(const T &other, const product_operator &self); + */ }; #ifndef CUDAQ_INSTANTIATE_TEMPLATES diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 9a6d5fd1c6..078c4252b8 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -262,8 +262,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto sum = self + other; auto reverse = other + self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}); @@ -284,8 +284,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto sum = self + other; auto reverse = other + self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); @@ -305,8 +305,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}); @@ -325,8 +325,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); @@ -402,7 +402,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto other = cudaq::matrix_operator::create(0); auto sum = self + other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = utils::annihilate_matrix(level_count) + @@ -416,7 +416,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto other = cudaq::matrix_operator::create(1); auto sum = self + other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto annihilate_full = cudaq::kronecker(utils::id_matrix(level_count), @@ -434,7 +434,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto other = cudaq::matrix_operator::create(0); auto sum = self - other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = utils::annihilate_matrix(level_count) - @@ -448,7 +448,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto other = cudaq::matrix_operator::create(1); auto sum = self - other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto annihilate_full = cudaq::kronecker(utils::id_matrix(level_count), @@ -466,7 +466,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto other = cudaq::matrix_operator::create(0); auto product = self * other; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -483,7 +483,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto other = cudaq::matrix_operator::create(1); auto product = self * other; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -515,8 +515,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { auto got = self + operator_sum; auto reverse = operator_sum + self; - ASSERT_TRUE(got.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(got.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); @@ -543,8 +543,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { auto got = self - operator_sum; auto reverse = operator_sum - self; - ASSERT_TRUE(got.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(got.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); @@ -571,12 +571,12 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { auto got = self * operator_sum; auto reverse = operator_sum * self; - ASSERT_TRUE(got.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(got.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); for (auto &term : got.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == 2); for (auto &term : reverse.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == 2); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); @@ -601,7 +601,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { cudaq::matrix_operator::identity(1); operator_sum += cudaq::matrix_operator::displace(0); - ASSERT_TRUE(operator_sum.n_terms() == 3); + ASSERT_TRUE(operator_sum.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), @@ -622,7 +622,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { cudaq::matrix_operator::identity(1); operator_sum -= cudaq::matrix_operator::annihilate(0); - ASSERT_TRUE(operator_sum.n_terms() == 3); + ASSERT_TRUE(operator_sum.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); @@ -644,9 +644,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { operator_sum *= self; - ASSERT_TRUE(operator_sum.n_terms() == 2); + ASSERT_TRUE(operator_sum.num_terms() == 2); for (auto &term : operator_sum.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == 2); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index 065d7cf2e6..20809616d6 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -26,15 +26,17 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { cudaq::operator_sum sum, cudaq::matrix_2 expected) { auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); utils::checkEqual(got, expected); }; auto checkProductEquals = [dimensions, parameters]( cudaq::product_operator prod, - cudaq::matrix_2 expected) { + cudaq::matrix_2 expected, bool aggregated_prod = false) { + auto expected_num_terms = 2; + if (aggregated_prod) expected_num_terms = prod.degrees().size(); auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.n_terms() == 2); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); utils::checkEqual(got, expected); }; @@ -67,7 +69,7 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { // `elementary * elementary` { checkProductEquals(matrix_elementary * matrix_elementary, matrix_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * spin_elementary, spin_elementary_expected * spin_elementary_expected); + checkProductEquals(spin_elementary * spin_elementary, spin_elementary_expected * spin_elementary_expected, true); checkProductEquals(boson_elementary * boson_elementary, boson_elementary_expected * boson_elementary_expected); checkProductEquals(matrix_elementary * spin_elementary, matrix_elementary_expected * spin_elementary_expected); checkProductEquals(spin_elementary * matrix_elementary, spin_elementary_expected * matrix_elementary_expected); @@ -85,7 +87,7 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { auto spin_product = cudaq::product_operator(spin_elementary); spin_product *= spin_elementary; - checkProductEquals(spin_product, spin_elementary_expected * spin_elementary_expected); + checkProductEquals(spin_product, spin_elementary_expected * spin_elementary_expected, true); auto boson_product = cudaq::product_operator(boson_elementary); boson_product *= boson_elementary; @@ -116,15 +118,17 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { cudaq::operator_sum sum, cudaq::matrix_2 expected) { auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); utils::checkEqual(got, expected); }; auto checkProductEquals = [dimensions, parameters]( cudaq::product_operator prod, - cudaq::matrix_2 expected) { + cudaq::matrix_2 expected, bool aggregated_prod = false) { + auto expected_num_terms = 4; + if (aggregated_prod) expected_num_terms = prod.degrees().size(); auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.n_terms() == 4); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); utils::checkEqual(got, expected); }; @@ -157,7 +161,7 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { // `product * product` { checkProductEquals(matrix_product * matrix_product, matrix_product_expected * matrix_product_expected); - checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected); + checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected, true); checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected); checkProductEquals(matrix_product * spin_product, matrix_product_expected * spin_product_expected); checkProductEquals(spin_product * matrix_product, spin_product_expected * matrix_product_expected); @@ -175,7 +179,7 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { auto spin_product_0 = spin_product; spin_product_0 *= spin_product; - checkProductEquals(spin_product_0, spin_product_expected * spin_product_expected); + checkProductEquals(spin_product_0, spin_product_expected * spin_product_expected, true); auto boson_product_0 = boson_product; boson_product_0 *= boson_product; @@ -217,7 +221,7 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { cudaq::operator_sum sum, cudaq::matrix_2 expected, int num_terms = 4) { auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.n_terms() == num_terms); + ASSERT_TRUE(sum.num_terms() == num_terms); utils::checkEqual(got, expected); }; diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 31dc551215..15e48d4850 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -265,8 +265,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + double_value; auto reverse = double_value + original; - ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); @@ -290,8 +290,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + value; auto reverse = value + original; - ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); @@ -315,8 +315,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + value; auto reverse = value + original; - ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = sum.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); @@ -341,8 +341,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; - ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); @@ -370,8 +370,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - double_value; auto reverse = double_value - original; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(difference.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); @@ -397,8 +397,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - double_value; auto reverse = double_value - original; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(difference.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = difference.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); @@ -424,8 +424,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - value; auto reverse = value - original; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(difference.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); @@ -451,8 +451,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(difference.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); @@ -479,16 +479,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * double_value; auto reverse = double_value * sum; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } @@ -514,16 +514,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * value; auto reverse = value * sum; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } @@ -549,16 +549,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } @@ -589,16 +589,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); for (auto term : product.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } for (auto term : reverse.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } @@ -623,9 +623,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= double_value; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } @@ -651,9 +651,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= double_value; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); } @@ -673,9 +673,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= value; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } @@ -699,9 +699,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum *= cudaq::scalar_operator(value); - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 1); + ASSERT_TRUE(term.num_terms() == 1); ASSERT_TRUE(term.get_coefficient().evaluate() == value); } @@ -729,7 +729,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += double_value; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); @@ -749,7 +749,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { cudaq::spin_operator::y(2); sum += double_value; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{1, 2}, {2, 2}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(2), @@ -769,7 +769,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += value; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); @@ -791,7 +791,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum += cudaq::scalar_operator(value); - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count}, {2, level_count + 1}}); @@ -817,7 +817,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= double_value; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); @@ -840,7 +840,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= value; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); @@ -861,7 +861,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { sum -= cudaq::scalar_operator(value); - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count}, {2, level_count + 1}}); @@ -886,7 +886,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { cudaq::spin_operator::y(2); sum -= cudaq::scalar_operator(value); - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix(); @@ -919,7 +919,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum += product; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { @@ -960,7 +960,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum -= product; - ASSERT_TRUE(sum.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); std::vector matrices_0_0 = { @@ -1001,9 +1001,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { sum *= product; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { - ASSERT_TRUE(term.n_terms() == 3); + ASSERT_TRUE(term.num_terms() == 3); } auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); @@ -1051,7 +1051,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto sum = sum_0 + sum_1; - ASSERT_TRUE(sum.n_terms() == 5); + ASSERT_TRUE(sum.num_terms() == 5); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count + 1}, @@ -1107,7 +1107,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto difference = sum_0 - sum_1; - ASSERT_TRUE(difference.n_terms() == 5); + ASSERT_TRUE(difference.num_terms() == 5); auto got_matrix = difference.to_matrix({{0, level_count}, {1, level_count + 1}, @@ -1164,12 +1164,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { auto sum_product = sum_0 * sum_1; auto sum_product_reverse = sum_1 * sum_0; - ASSERT_TRUE(sum_product.n_terms() == 6); - ASSERT_TRUE(sum_product_reverse.n_terms() == 6); + ASSERT_TRUE(sum_product.num_terms() == 6); + ASSERT_TRUE(sum_product_reverse.num_terms() == 6); for (auto term : sum_product.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == 2); for (auto term : sum_product_reverse.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == 2); auto got_matrix = sum_product.to_matrix({{0, level_count}, {1, level_count + 1}, @@ -1232,9 +1232,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { sum *= sum_1; - ASSERT_TRUE(sum.n_terms() == 6); + ASSERT_TRUE(sum.num_terms() == 6); for (auto term : sum.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == 2); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count + 1}, diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index 4b950bdf4e..f17dd9baf3 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -269,8 +269,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = 2.0 + product_op; auto reverse = product_op + 2.0; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -301,8 +301,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = value_0 + product_op; auto reverse = product_op + value_0; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -334,8 +334,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = value_0 + product_op; auto reverse = product_op + value_0; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -367,8 +367,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = scalar_op + product_op; auto reverse = product_op + scalar_op; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -401,8 +401,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = 2.0 - product_op; auto reverse = product_op - 2.0; - ASSERT_TRUE(difference.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(difference.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); @@ -433,8 +433,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto sum = 2.0 - product_op; auto reverse = product_op - 2.0; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -465,8 +465,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = value_0 - product_op; auto reverse = product_op - value_0; - ASSERT_TRUE(difference.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(difference.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); @@ -501,8 +501,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto difference = scalar_op - product_op; auto reverse = product_op - scalar_op; - ASSERT_TRUE(difference.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(difference.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(difference.degrees() == want_degrees); @@ -532,14 +532,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { { auto product_op = cudaq::matrix_operator::parity(0) * cudaq::matrix_operator::parity(1); - ASSERT_TRUE(product_op.n_terms() == 2); + ASSERT_TRUE(product_op.num_terms() == 2); ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); @@ -566,17 +566,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * complex` { - auto product_op = - cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); - ASSERT_TRUE(product_op.n_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == - std::complex(1.)); + auto product_op = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::number(1); + ASSERT_TRUE(product_op.num_terms() == 2); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); ASSERT_TRUE(reverse.get_coefficient().evaluate() == value_0); @@ -612,8 +611,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); @@ -649,8 +648,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto product = scalar_op * product_op; auto reverse = product_op * scalar_op; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(reverse.get_coefficient().evaluate() == scalar_op.evaluate()); @@ -682,9 +681,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::matrix_operator::create(1); product *= 2.0; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == - std::complex(2.)); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -709,7 +707,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::spin_operator::i(1); product *= 2.0; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); std::vector want_degrees = {1, 0}; @@ -735,7 +733,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { cudaq::matrix_operator::momentum(1); product *= value_0; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == value_0); std::vector want_degrees = {1, 0}; @@ -764,7 +762,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= scalar_op; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); ASSERT_TRUE(product.get_coefficient().evaluate() == scalar_op.evaluate()); ASSERT_TRUE(scalar_op.evaluate() == value_0); @@ -800,7 +798,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto sum = term_0 + term_1; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); std::vector want_degrees = {2, 1, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -845,7 +843,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto sum = term_0 + term_1; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); std::vector want_degrees = {4, 2, 0}; ASSERT_TRUE(sum.degrees() == want_degrees); @@ -890,7 +888,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto difference = term_0 - term_1; - ASSERT_TRUE(difference.n_terms() == 2); + ASSERT_TRUE(difference.num_terms() == 2); auto got_matrix = difference.to_matrix(dimensions); @@ -932,8 +930,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto difference = term_0 - term_1; auto reverse = term_1 - term_0; - ASSERT_TRUE(difference.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(difference.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto got_matrix = difference.to_matrix(); auto reverse_matrix = reverse.to_matrix(); @@ -973,7 +971,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto product = term_0 * term_1; - ASSERT_TRUE(product.n_terms() == 4); + ASSERT_TRUE(product.num_terms() == 4); auto got_matrix = product.to_matrix(dimensions); @@ -1017,8 +1015,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto reverse = term_1 * term_0; std::vector expected_degrees = {3, 1, 0}; - ASSERT_TRUE(product.n_terms() == 4); - ASSERT_TRUE(reverse.n_terms() == 4); + ASSERT_TRUE(product.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); ASSERT_TRUE(product.degrees() == expected_degrees); auto got_matrix = product.to_matrix(); @@ -1064,7 +1062,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { term_0 *= term_1; - ASSERT_TRUE(term_0.n_terms() == 4); + ASSERT_TRUE(term_0.num_terms() == 4); auto got_matrix = term_0.to_matrix(dimensions); @@ -1108,7 +1106,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { term_0 *= term_1; - ASSERT_TRUE(term_0.n_terms() == 4); + ASSERT_TRUE(term_0.num_terms() == 3); auto got_matrix = term_0.to_matrix(); @@ -1160,8 +1158,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto sum = product + original_sum; auto reverse = original_sum + product; - ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = sum.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); @@ -1203,8 +1201,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto sum = product + original_sum; auto reverse = original_sum + product; - ASSERT_TRUE(sum.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(sum.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = sum.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); @@ -1241,8 +1239,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto difference = product - original_difference; auto reverse = original_difference - product; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(difference.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = difference.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); @@ -1284,8 +1282,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto difference = product - original_difference; auto reverse = original_difference - product; - ASSERT_TRUE(difference.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(difference.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto got_matrix = difference.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); @@ -1322,8 +1320,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto product = original_product * sum; auto reverse = sum * original_product; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto got_matrix = product.to_matrix(dimensions); auto got_matrix_reverse = reverse.to_matrix(dimensions); @@ -1365,8 +1363,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { auto product = original_product * sum; auto reverse = sum * original_product; - ASSERT_TRUE(product.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto got_matrix = product.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp index 8de87773bf..6c4d7afe75 100644 --- a/unittests/dynamics/spin_operator.cpp +++ b/unittests/dynamics/spin_operator.cpp @@ -137,8 +137,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto sum = self + other; auto reverse = other + self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = sum.to_matrix(); @@ -157,8 +157,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto sum = self + other; auto reverse = other + self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = sum.to_matrix({}, {{"value", const_scale_factor}}); @@ -177,8 +177,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = sum.to_matrix(); @@ -197,8 +197,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto sum = self - other; auto reverse = other - self; - ASSERT_TRUE(sum.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = sum.to_matrix({}, {{"value", const_scale_factor}}); @@ -260,7 +260,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto other = cudaq::spin_operator::y(0); auto sum = self + other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(); auto want_matrix = utils::PauliX_matrix() + @@ -274,7 +274,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto other = cudaq::spin_operator::y(1); auto sum = self + other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto matrix_self = cudaq::kronecker(utils::id_matrix(2), utils::PauliZ_matrix()); @@ -291,7 +291,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto other = cudaq::spin_operator::x(0); auto sum = self - other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(); auto want_matrix = utils::PauliZ_matrix() - @@ -305,7 +305,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto other = cudaq::spin_operator::x(1); auto sum = self - other; - ASSERT_TRUE(sum.n_terms() == 2); + ASSERT_TRUE(sum.num_terms() == 2); auto annihilate_full = cudaq::kronecker(utils::id_matrix(2), @@ -323,7 +323,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto other = cudaq::spin_operator::z(0); auto product = self * other; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 1); std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -340,7 +340,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto other = cudaq::spin_operator::z(1); auto product = self * other; - ASSERT_TRUE(product.n_terms() == 2); + ASSERT_TRUE(product.num_terms() == 2); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -358,11 +358,10 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { - /// Keeping this fixed throughout. + // Keeping this fixed throughout. std::complex value = 0.125 + 0.5j; - /// `spin_operator + operator_sum` and `operator_sum + - /// spin_operator` + // `spin_operator + operator_sum` { auto self = cudaq::spin_operator::y(2); auto operator_sum = cudaq::spin_operator::y(2) + @@ -371,8 +370,8 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { auto got = self + operator_sum; auto reverse = operator_sum + self; - ASSERT_TRUE(got.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(got.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto self_full = cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); @@ -389,8 +388,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `spin_operator - operator_sum` and `operator_sum - - /// spin_operator` + // `spin_operator - operator_sum` { auto self = cudaq::spin_operator::i(0); auto operator_sum = cudaq::spin_operator::x(0) + @@ -399,8 +397,8 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { auto got = self - operator_sum; auto reverse = operator_sum - self; - ASSERT_TRUE(got.n_terms() == 3); - ASSERT_TRUE(reverse.n_terms() == 3); + ASSERT_TRUE(got.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); @@ -417,8 +415,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `spin_operator * operator_sum` and `operator_sum * - /// spin_operator` + // `spin_operator * operator_sum` { auto self = cudaq::spin_operator::y(0); auto operator_sum = cudaq::spin_operator::x(0) + @@ -427,12 +424,12 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { auto got = self * operator_sum; auto reverse = operator_sum * self; - ASSERT_TRUE(got.n_terms() == 2); - ASSERT_TRUE(reverse.n_terms() == 2); + ASSERT_TRUE(got.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); for (auto &term : got.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == term.degrees().size()); for (auto &term : reverse.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == term.degrees().size()); auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); @@ -451,13 +448,13 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `operator_sum += spin_operator` + // `operator_sum += spin_operator` { auto operator_sum = cudaq::spin_operator::z(0) + cudaq::spin_operator::x(2); operator_sum += cudaq::spin_operator::y(0); - ASSERT_TRUE(operator_sum.n_terms() == 3); + ASSERT_TRUE(operator_sum.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(2), @@ -472,13 +469,13 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { utils::checkEqual(want_matrix, got_matrix); } - /// `operator_sum -= spin_operator` + // `operator_sum -= spin_operator` { auto operator_sum = cudaq::spin_operator::x(0) + cudaq::spin_operator::i(1); operator_sum -= cudaq::spin_operator::x(0); - ASSERT_TRUE(operator_sum.n_terms() == 3); + ASSERT_TRUE(operator_sum.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); @@ -492,7 +489,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { utils::checkEqual(want_matrix, got_matrix); } - /// `operator_sum *= spin_operator` + // `operator_sum *= spin_operator` { auto self = cudaq::spin_operator::i(0); auto operator_sum = cudaq::spin_operator::y(0) + @@ -500,9 +497,9 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { operator_sum *= self; - ASSERT_TRUE(operator_sum.n_terms() == 2); + ASSERT_TRUE(operator_sum.num_terms() == 2); for (auto &term : operator_sum.get_terms()) - ASSERT_TRUE(term.n_terms() == 2); + ASSERT_TRUE(term.num_terms() == term.degrees().size()); auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); From de4950241e369747d37e5882b39600f58f57312d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 13 Feb 2025 01:15:34 +0000 Subject: [PATCH 267/311] first version with perf improvements Signed-off-by: Bettina Heim --- main.cpp | 187 ++++++++++-- runtime/cudaq/dynamics/boson_operators.cpp | 32 +- runtime/cudaq/dynamics/boson_operators.h | 21 +- runtime/cudaq/dynamics/callback.cpp | 17 +- runtime/cudaq/dynamics/callback.h | 28 +- runtime/cudaq/dynamics/helpers.cpp | 151 +++------- runtime/cudaq/dynamics/helpers.h | 31 ++ runtime/cudaq/dynamics/manipulation.cpp | 57 ++-- runtime/cudaq/dynamics/manipulation.h | 18 +- runtime/cudaq/dynamics/matrix_operators.cpp | 102 ++++--- runtime/cudaq/dynamics/matrix_operators.h | 22 +- runtime/cudaq/dynamics/operator_leafs.h | 31 +- runtime/cudaq/dynamics/operator_sum.cpp | 290 ++++++++++++------- runtime/cudaq/dynamics/product_operators.cpp | 156 +++++++--- runtime/cudaq/dynamics/scalar_operators.cpp | 29 +- runtime/cudaq/dynamics/spin_operators.cpp | 35 +-- runtime/cudaq/dynamics/spin_operators.h | 23 +- runtime/cudaq/operators.h | 64 +++- unittests/dynamics/matrix_operator.cpp | 37 ++- unittests/dynamics/operator_conversions.cpp | 81 +++--- unittests/dynamics/operator_sum.cpp | 38 +-- unittests/dynamics/product_operator.cpp | 39 ++- unittests/dynamics/scalar_operator.cpp | 59 ++-- unittests/dynamics/spin_operator.cpp | 13 +- 24 files changed, 938 insertions(+), 623 deletions(-) create mode 100644 runtime/cudaq/dynamics/helpers.h diff --git a/main.cpp b/main.cpp index 624d93f25d..2ee50a889f 100644 --- a/main.cpp +++ b/main.cpp @@ -10,13 +10,14 @@ #include #include #include +#include int main() { // multiplication inplace with itself - cudaq::spin_op spin_op = cudaq::spin::i(0); - cudaq::product_operator prod_op = cudaq::spin_operator::i(0); + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::product_operator prod_op = cudaq::spin_operator::identity(); int nr_reps = 1000; std::cout << "multiplication inplace with itself" << std::endl; @@ -37,8 +38,8 @@ int main() { // multiplication inplace with other - spin_op = cudaq::spin::i(0); - prod_op = cudaq::spin_operator::i(0); + spin_op = cudaq::spin_op(); + prod_op = cudaq::spin_operator::identity(); nr_reps = 1000; std::cout << "multiplication inplace with other" << std::endl; @@ -57,13 +58,32 @@ int main() { duration = std::chrono::duration(stop - start); std::cout << "New setup took " << duration.count() << " seconds.\n"; + // multiplication inplace with other (reverse order) + + spin_op = cudaq::spin_op(); + prod_op = cudaq::spin_operator::identity(); + + nr_reps = 1000; + std::cout << "multiplication inplace with other (reverse order)" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + // addition inplace with itself - spin_op = cudaq::spin::i(0); - // auto op_sum = cudaq::operator_sum(cudaq::spin_operator::i(0)); // fixme: protected - // cudaq::operator_sum op_sum = cudaq::spin_operator::i(0); // fixme: protected - prod_op = cudaq::spin_operator::i(0); - cudaq::operator_sum op_sum = prod_op; + spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); nr_reps = 1000; std::cout << "addition inplace with itself" << std::endl; @@ -84,9 +104,8 @@ int main() { // addition inplace with other - spin_op = cudaq::spin::i(0); - prod_op = cudaq::spin_operator::i(0); - op_sum = prod_op; + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::empty(); nr_reps = 1000; std::cout << "addition inplace with other" << std::endl; @@ -105,14 +124,36 @@ int main() { duration = std::chrono::duration(stop - start); std::cout << "New setup took " << duration.count() << " seconds.\n"; + // addition inplace with other (reverse order) + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::empty(); + + nr_reps = 1000; + std::cout << "addition inplace with other (reverse order)" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + // addition inplace with product self - auto spin_prod = cudaq::spin::i(0); + auto spin_prod = cudaq::spin_op(); for (auto i = 0; i < 100; ++i) spin_prod *= cudaq::spin::x(i); spin_op = spin_prod; - prod_op = cudaq::spin_operator::i(0); + prod_op = cudaq::spin_operator::identity(); for (auto i = 0; i < 100; ++i) prod_op *= cudaq::spin_operator::x(i); op_sum = prod_op; @@ -134,6 +175,34 @@ int main() { duration = std::chrono::duration(stop - start); std::cout << "New setup took " << duration.count() << " seconds.\n"; + // addition inplace with product self (reverse order) + + spin_prod = cudaq::spin_op(); + for (auto i = 0; i < 100; ++i) + spin_prod *= cudaq::spin::x(100 - i); + spin_op = spin_prod; + prod_op = cudaq::spin_operator::identity(); + for (auto i = 0; i < 100; ++i) + prod_op *= cudaq::spin_operator::x(100 - i); + op_sum = prod_op; + + nr_reps = 1000; + std::cout << "addition inplace with product self" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + // product inplace with 2-term sum on fixed degrees auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); @@ -160,23 +229,103 @@ int main() { // product inplace with 2-term sum on varying degrees - spin_op = cudaq::spin::i(0); - prod_op = cudaq::spin_operator::i(0); - op_sum = prod_op; + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::identity(); nr_reps = 20; std::cout << "product inplace with 2-term sum on varying degrees" << std::endl; start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(i) + cudaq::spin::z(i + 1); + spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // product inplace with 2-term sum on varying degrees (reverse order) + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::identity(); + + nr_reps = 20; + std::cout << "product inplace with 2-term sum on varying degrees (reverse order)" << std::endl; + + /* + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - 1)); stop = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration(stop - start); std::cout << "Old setup took " << duration.count() << " seconds.\n"; + */ + std::cout << "Old setup segfaults" << std::endl; start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) - op_sum *= cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1); + op_sum *= (cudaq::spin_operator::x(nr_reps - i) + cudaq::spin_operator::z(nr_reps - i - 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + + // sum of products of random terms + + auto nr_degrees = 100; + std::vector spin_ops; + for (auto i = 0; i < nr_degrees; ++i) { + spin_ops.push_back(cudaq::spin::x(i)); + spin_ops.push_back(cudaq::spin::y(i)); + spin_ops.push_back(cudaq::spin::z(i)); + spin_ops.push_back(cudaq::spin::i(i)); + } + std::vector> leaf_ops; + for (auto i = 0; i < nr_degrees; ++i) { + leaf_ops.push_back(cudaq::spin_operator::x(i)); + leaf_ops.push_back(cudaq::spin_operator::y(i)); + leaf_ops.push_back(cudaq::spin_operator::z(i)); + leaf_ops.push_back(cudaq::spin_operator::i(i)); + } + + auto term_length = 100; + auto nr_terms = 20; + srand(5); // random number seed + std::vector> indices; + for (auto i = 0; i < nr_terms; ++i) { + indices.push_back({}); + for (auto j = 0; j < term_length; ++j) + indices[i].push_back(rand() % 400); + } + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::empty(); + + std::cout << "sum of products of random terms" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_terms; ++i) { + auto term = cudaq::spin_op(); + for (auto j = 0; j < term_length; ++j) + term *= spin_ops[indices[i][j]]; + spin_op += term; + } + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_terms; ++i) { + auto term = cudaq::spin_operator::identity(); + for (auto j = 0; j < term_length; ++j) + term *= leaf_ops[indices[i][j]]; + op_sum += term; + } stop = std::chrono::high_resolution_clock::now(); duration = std::chrono::duration(stop - start); std::cout << "New setup took " << duration.count() << " seconds.\n"; diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 945b6d0e08..fa502a78c9 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include -#include +#include #include #include "cudaq/utils/tensor.h" @@ -24,19 +24,18 @@ std::vector boson_operator::degrees() const { // constructors -boson_operator::boson_operator(int op_id, int target) +boson_operator::boson_operator(int target) + : id(0), target(target) {} + +boson_operator::boson_operator(int target, int op_id) : id(op_id), target(target) { assert(0 <= op_id < 4); } -bool boson_operator::is_identity() const { - return this->id == 0; -} - // evaluations -matrix_2 boson_operator::to_matrix(std::map &dimensions, - std::map> parameters) const { +matrix_2 boson_operator::to_matrix(std::unordered_map &dimensions, + const std::unordered_map> ¶meters) const { auto it = dimensions.find(this->target); if (it == dimensions.end()) throw std::runtime_error("missing dimension for degree " + std::to_string(this->target)); @@ -77,25 +76,28 @@ bool boson_operator::operator==(const boson_operator &other) const { // defined operators -// multiplicative identity -boson_operator boson_operator::one(int degree) { - return boson_operator(0, degree); +operator_sum boson_operator::empty() { + return operator_handler::empty(); +} + +product_operator boson_operator::identity() { + return operator_handler::identity(); } product_operator boson_operator::identity(int degree) { - return product_operator(boson_operator(0, degree)); + return product_operator(boson_operator(degree)); } product_operator boson_operator::create(int degree) { - return product_operator(boson_operator(1, degree)); + return product_operator(boson_operator(degree, 1)); } product_operator boson_operator::annihilate(int degree) { - return product_operator(boson_operator(2, degree)); + return product_operator(boson_operator(degree, 2)); } product_operator boson_operator::number(int degree) { - return product_operator(boson_operator(3, degree)); + return product_operator(boson_operator(degree, 3)); } // FIXME: add position, momentum, others? diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index 6c7bc0dc5b..e6426764b5 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include #include "cudaq/utils/tensor.h" @@ -21,7 +21,7 @@ template class product_operator; // FIXME: rename? -class boson_operator : operator_handler{ +class boson_operator : public operator_handler{ private: @@ -29,7 +29,7 @@ class boson_operator : operator_handler{ int id; int target; - boson_operator(int op, int target); + boson_operator(int target, int op); public: @@ -39,13 +39,11 @@ class boson_operator : operator_handler{ /// order. virtual std::vector degrees() const; - virtual bool is_identity() const; - // constructors and destructors - ~boson_operator() = default; + boson_operator(int target); - // assignments + ~boson_operator() = default; // evaluations @@ -54,8 +52,8 @@ class boson_operator : operator_handler{ /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map &dimensions, - std::map> parameters = {}) const; + virtual matrix_2 to_matrix(std::unordered_map &dimensions, + const std::unordered_map> ¶meters = {}) const; virtual std::string to_string(bool include_degrees) const; @@ -65,8 +63,9 @@ class boson_operator : operator_handler{ // defined operators - // multiplicative identity - static boson_operator one(int degree); + static operator_sum empty(); + static product_operator identity(); + static product_operator identity(int degree); static product_operator create(int degree); static product_operator annihilate(int degree); diff --git a/runtime/cudaq/dynamics/callback.cpp b/runtime/cudaq/dynamics/callback.cpp index c04da8752d..6d4e461fed 100644 --- a/runtime/cudaq/dynamics/callback.cpp +++ b/runtime/cudaq/dynamics/callback.cpp @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -40,8 +41,8 @@ ScalarCallbackFunction& ScalarCallbackFunction::operator=(ScalarCallbackFunction } std::complex -ScalarCallbackFunction::operator()(std::map> parameters) const { - return _callback_func(std::move(parameters)); +ScalarCallbackFunction::operator()(const std::unordered_map> ¶meters) const { + return _callback_func(parameters); } // MatrixCallbackFunction @@ -69,22 +70,22 @@ MatrixCallbackFunction& MatrixCallbackFunction::operator=(MatrixCallbackFunction } matrix_2 -MatrixCallbackFunction::operator()(std::vector relevant_dimensions, - std::map> parameters) const { - return _callback_func(std::move(relevant_dimensions), std::move(parameters)); +MatrixCallbackFunction::operator()(const std::vector &relevant_dimensions, + const std::unordered_map> ¶meters) const { + return _callback_func(relevant_dimensions, parameters); } // Definition -Definition::Definition(const std::string &operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create) - : id(operator_id), generator(std::move(create)), m_expected_dimensions(std::move(expected_dimensions)) {} +Definition::Definition(std::string operator_id, const std::vector &expected_dimensions, MatrixCallbackFunction &&create) + : id(operator_id), generator(std::move(create)), m_expected_dimensions(expected_dimensions) {} Definition::Definition(Definition &&def) : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( const std::vector &relevant_dimensions, - const std::map> ¶meters) const { + const std::unordered_map> ¶meters) const { return generator(relevant_dimensions, parameters); } diff --git a/runtime/cudaq/dynamics/callback.h b/runtime/cudaq/dynamics/callback.h index 6ca9ea9da8..903276b9ff 100644 --- a/runtime/cudaq/dynamics/callback.h +++ b/runtime/cudaq/dynamics/callback.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -24,16 +24,16 @@ class ScalarCallbackFunction { private: // The user provided callback function that takes a map of complex // parameters. - std::function(std::map>)> _callback_func; + std::function(const std::unordered_map>&)> _callback_func; public: template ScalarCallbackFunction(Callable &&callable) { static_assert( std::is_invocable_r_v, Callable, - std::map>>, + const std::unordered_map>&>, "Invalid callback function. Must have signature std::complex(" - "std::map>)"); + "const std::unordered_map>&)"); _callback_func = std::forward(callable); } @@ -50,7 +50,7 @@ class ScalarCallbackFunction { ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other); std::complex - operator()(std::map> parameters) const; + operator()(const std::unordered_map> ¶meters) const; }; @@ -59,18 +59,16 @@ class MatrixCallbackFunction { // The user provided callback function that takes a vector defining the // dimension for each degree of freedom it acts on, and a map of complex // parameters. - std::function, std::map>)> _callback_func; + std::function&, const std::unordered_map>&)> _callback_func; public: template MatrixCallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v, - std::map>>, + std::is_invocable_r_v&, + const std::unordered_map>&>, "Invalid callback function. Must have signature " - "matrix_2(" - "std::map, " - "std::map>)"); + "matrix_2(const std::vector&, const std::unordered_map>&)"); _callback_func = std::forward(callable); } @@ -87,8 +85,8 @@ class MatrixCallbackFunction { MatrixCallbackFunction& operator=(MatrixCallbackFunction &&other); matrix_2 - operator()(std::vector relevant_dimensions, - std::map> parameters) const; + operator()(const std::vector &relevant_dimensions, + const std::unordered_map> ¶meters) const; }; @@ -102,13 +100,13 @@ class Definition { public: const std::vector& expected_dimensions = this->m_expected_dimensions; - Definition(const std::string &operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create); + Definition(std::string operator_id, const std::vector &expected_dimensions, MatrixCallbackFunction &&create); Definition(Definition &&def); ~Definition(); // To call the generator function matrix_2 generate_matrix( const std::vector &relevant_dimensions, - const std::map> ¶meters) const; + const std::unordered_map> ¶meters) const; }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index 2b5c3413c4..de927daaa5 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -6,140 +6,57 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/helpers.h" -#include "cudaq/operators.h" -#include -#include -#include -#include -#include +#include +#include "helpers.h" namespace cudaq { namespace detail { -// Aggregate parameters from multiple mappings. -std::map aggregate_parameters( - const std::vector> ¶meter_mappings) { - std::map parameter_descriptions; + std::vector + generate_all_states(const std::vector °rees, const std::unordered_map &dimensions) { + if (degrees.size() == 0) + return {}; - for (const auto &descriptions : parameter_mappings) { - for (const auto &[key, new_desc] : descriptions) { - if (!parameter_descriptions[key].empty() && !new_desc.empty()) { - parameter_descriptions[key] += "\n---\n" + new_desc; - } else { - parameter_descriptions[key] = new_desc; - } + std::vector states; + auto entry = dimensions.find(degrees[0]); + assert (entry != dimensions.end()); + for (auto state = 0; state < entry->second; state++) { + states.push_back(std::to_string(state)); } - } - - return parameter_descriptions; -} - -// Extract documentation for a specific parameter from docstring. -std::string parameter_docs(const std::string ¶m_name, - const std::string &docs) { - if (param_name.empty() || docs.empty()) { - return ""; - } - - try { - std::regex keyword_pattern(R"(^\s*(Arguments|Args):\s*$)", - std::regex::multiline); - std::regex param_pattern(R"(^\s*)" + param_name + - R"(\s*(\(.*\))?:\s*(.*)$)", - std::regex::multiline); - std::smatch match; - std::sregex_iterator it(docs.begin(), docs.end(), keyword_pattern); - std::sregex_iterator end; - - if (it != end) { - std::string params_section = docs.substr(it->position() + it->length()); - if (std::regex_search(params_section, match, param_pattern)) { - std::string param_docs = match.str(2); - return std::regex_replace(param_docs, std::regex(R"(\s+)"), " "); + for (auto idx = 1; idx < degrees.size(); ++idx) { + auto entry = dimensions.find(degrees[idx]); + assert (entry != dimensions.end()); + std::vector result; + for (auto current : states) { + for (auto state = 0; state < entry->second; state++) { + result.push_back(current + std::to_string(state)); + } } + states = result; } - } catch (...) { - return ""; - } - - return ""; -} -// Extract positional arguments and keyword-only arguments. -std::pair, std::map> -args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args) { - std::vector extracted_args; - std::map kwonly_dict; - - for (const auto &arg : required_args) { - if (kwargs.count(arg)) { - extracted_args.push_back(kwargs.at(arg)); - } else { - throw std::invalid_argument("Missing required argument: " + arg); - } + return states; } - for (const auto &arg : kwonly_args) { - if (kwargs.count(arg)) { - kwonly_dict[arg] = kwargs.at(arg); + void permute_matrix(cudaq::matrix_2 &matrix, const std::vector &permutation) { + std::vector> sorted_values; + for (std::size_t permuted : permutation) { + for (std::size_t permuted_again : permutation) { + sorted_values.push_back(matrix[{permuted, permuted_again}]); + } } - } - - return {extracted_args, kwonly_dict}; -} - -/// Generates all possible states for the given dimensions ordered according -/// to the sequence of degrees (ordering is relevant if dimensions differ). -std::vector generate_all_states(std::vector degrees, - std::map dimensions) { - if (degrees.size() == 0) - return {}; - - std::vector states; - int range = dimensions[degrees[0]]; - for (auto state = 0; state < range; state++) { - states.push_back(std::to_string(state)); - } - - for (auto idx = 1; idx < degrees.size(); ++idx) { - std::vector result; - for (auto current : states) { - for (auto state = 0; state < dimensions[degrees[idx]]; state++) { - result.push_back(current + std::to_string(state)); + int idx = 0; + for (std::size_t row = 0; row < matrix.get_rows(); row++) { + for (std::size_t col = 0; col < matrix.get_columns(); col++) { + matrix[{row, col}] = sorted_values[idx]; + idx++; } } - states = result; } - return states; -} - -cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation) { - auto result = cudaq::matrix_2(matrix.get_rows(), matrix.get_columns()); - std::vector> sorted_values; - for (std::size_t permuted : permutation) { - for (std::size_t permuted_again : permutation) { - sorted_values.push_back(matrix[{permuted, permuted_again}]); - } + void canonicalize_degrees(std::vector °rees) { + std::sort(degrees.begin(), degrees.end(), std::greater()); } - int idx = 0; - for (std::size_t row = 0; row < result.get_rows(); row++) { - for (std::size_t col = 0; col < result.get_columns(); col++) { - result[{row, col}] = sorted_values[idx]; - idx++; - } - } - return result; -} - -std::vector canonicalize_degrees(std::vector degrees) { - std::sort(degrees.begin(), degrees.end(), std::greater()); - return degrees; -} } // namespace detail } // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h new file mode 100644 index 0000000000..7c0836e4dc --- /dev/null +++ b/runtime/cudaq/dynamics/helpers.h @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include +#include "cudaq/utils/tensor.h" + +namespace cudaq { +namespace detail { + + /// Generates all possible states for the given dimensions ordered according + /// to the sequence of degrees (ordering is relevant if dimensions differ). + std::vector generate_all_states(const std::vector °rees, const std::unordered_map &dimensions); + + // Permutes the given matrix according to the given permutation. + // If states is the current order of vector entries on which the given matrix + // acts, and permuted_states is the desired order of an array on which the + // permuted matrix should act, then the permutation is defined such that + // [states[i] for i in permutation] produces permuted_states. + void permute_matrix(cudaq::matrix_2 &matrix, const std::vector &permutation); + + // Returns the degrees sorted in canonical order. + void canonicalize_degrees(std::vector °rees); +} +} + diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 841732a381..8346526a67 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -6,9 +6,11 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "manipulation.h" -#include "helpers.h" #include +#include + +#include "helpers.h" +#include "manipulation.h" namespace cudaq { @@ -45,19 +47,21 @@ EvaluatedMatrix& EvaluatedMatrix::operator=(EvaluatedMatrix &&other) { // MatrixArithmetics -MatrixArithmetics::MatrixArithmetics(std::map dimensions, - std::map> parameters) +MatrixArithmetics::MatrixArithmetics( + std::unordered_map &dimensions, + const std::unordered_map> ¶meters) : m_dimensions(dimensions), m_parameters(parameters) {} std::vector -MatrixArithmetics::_compute_permutation(std::vector op_degrees, - std::vector canon_degrees) { +MatrixArithmetics::compute_permutation(const std::vector &op_degrees, + const std::vector &canon_degrees) { + assert(op_degrees.size() == canon_degrees.size()); auto states = cudaq::detail::generate_all_states(canon_degrees, m_dimensions); std::vector reordering; for (auto degree : op_degrees) { - auto it = std::find(canon_degrees.begin(), canon_degrees.end(), degree); - reordering.push_back(it - canon_degrees.begin()); + auto it = std::find(canon_degrees.cbegin(), canon_degrees.cend(), degree); + reordering.push_back(it - canon_degrees.cbegin()); } std::vector op_states = @@ -69,8 +73,8 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, for (auto i : reordering) { term += state[i]; } - auto it = std::find(op_states.begin(), op_states.end(), term); - permutation.push_back(it - op_states.begin()); + auto it = std::find(op_states.cbegin(), op_states.cend(), term); + permutation.push_back(it - op_states.cbegin()); } return permutation; @@ -81,33 +85,30 @@ MatrixArithmetics::_compute_permutation(std::vector op_degrees, // Returns: // A tuple consisting of the permuted matrix as well as the sequence of // degrees of freedom in canonical order. -std::tuple> -MatrixArithmetics::_canonicalize(matrix_2 &op_matrix, - std::vector op_degrees) { - auto canon_degrees = cudaq::detail::canonicalize_degrees(op_degrees); - if (op_degrees == canon_degrees) - return std::tuple>{op_matrix, canon_degrees}; - - auto permutation = this->_compute_permutation(op_degrees, canon_degrees); - auto result = cudaq::detail::permute_matrix(op_matrix, permutation); - return std::tuple>{result, canon_degrees}; +void MatrixArithmetics::canonicalize(matrix_2 &matrix, std::vector °rees) { + auto current_degrees = degrees; + cudaq::detail::canonicalize_degrees(degrees); + if (current_degrees != degrees) { + auto permutation = this->compute_permutation(current_degrees, degrees); + cudaq::detail::permute_matrix(matrix, permutation); + } } EvaluatedMatrix MatrixArithmetics::tensor(EvaluatedMatrix op1, EvaluatedMatrix op2) { - std::vector op_degrees; + std::vector degrees; auto op1_degrees = op1.degrees(); auto op2_degrees = op2.degrees(); - op_degrees.reserve(op1_degrees.size() + op2_degrees.size()); + degrees.reserve(op1_degrees.size() + op2_degrees.size()); for (auto d : op1_degrees) - op_degrees.push_back(d); + degrees.push_back(d); for (auto d : op2_degrees) { - assert(std::find(op_degrees.begin(), op_degrees.end(), d) == op_degrees.end()); - op_degrees.push_back(d); + assert(std::find(degrees.cbegin(), degrees.cend(), d) == degrees.cend()); + degrees.push_back(d); } - auto op_matrix = cudaq::kronecker(op1.matrix(), op2.matrix()); - auto [new_matrix, new_degrees] = this->_canonicalize(op_matrix, op_degrees); - return EvaluatedMatrix(new_degrees, new_matrix); + auto matrix = cudaq::kronecker(op1.matrix(), op2.matrix()); + this->canonicalize(matrix, degrees); + return EvaluatedMatrix(std::move(degrees), std::move(matrix)); } EvaluatedMatrix MatrixArithmetics::mul(EvaluatedMatrix op1, diff --git a/runtime/cudaq/dynamics/manipulation.h b/runtime/cudaq/dynamics/manipulation.h index 1ce127da8b..f74ba54c95 100644 --- a/runtime/cudaq/dynamics/manipulation.h +++ b/runtime/cudaq/dynamics/manipulation.h @@ -8,7 +8,7 @@ #pragma once -#include +#include #include #include "cudaq/utils/tensor.h" @@ -58,17 +58,17 @@ class EvaluatedMatrix { /// of an operator expression. class MatrixArithmetics : public OperatorArithmetics { private: - std::vector _compute_permutation(std::vector op_degrees, - std::vector canon_degrees); - std::tuple> - _canonicalize(matrix_2 &op_matrix, std::vector op_degrees); + std::vector compute_permutation(const std::vector &op_degrees, + const std::vector &canon_degrees); + + void canonicalize(matrix_2 &op_matrix, std::vector &op_degrees); public: - std::map &m_dimensions; // fixme: make const - std::map> &m_parameters; // fixme: make const + std::unordered_map m_dimensions; // may be updated during evaluation + const std::unordered_map> m_parameters; - MatrixArithmetics(std::map dimensions, - std::map> parameters); + MatrixArithmetics(std::unordered_map &dimensions, + const std::unordered_map> ¶meters); // Computes the tensor product of two evaluate operators that act on // different degrees of freedom using the kronecker product. diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 7cad248f61..be84df493c 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include -#include +#include #include #include "cudaq/utils/tensor.h" @@ -18,9 +18,13 @@ namespace cudaq { +#if !defined(NDEBUG) +bool matrix_operator::can_be_canonicalized = false; +#endif + // tools for custom operators -std::map matrix_operator::m_ops = {}; +std::unordered_map matrix_operator::m_ops = {}; void matrix_operator::define(std::string operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create) { @@ -51,12 +55,28 @@ std::vector matrix_operator::degrees() const { return this->targets; } -bool matrix_operator::is_identity() const { - return this->id == "identity"; -} - // constructors +matrix_operator::matrix_operator(int degree) { + std::string op_id = "identity"; + if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + + // Build up the identity matrix. + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = 1.0 + 0.0j; + } + return mat; + }; + matrix_operator::define(op_id, {-1}, std::move(func)); + } + this->id = op_id; + this->targets.push_back(degree); +} + matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) : id(operator_id), targets(degrees) { assert(this->targets.size() > 0); @@ -72,9 +92,9 @@ matrix_operator::matrix_operator(const T &other) { this->targets = other.degrees(); this->id = typeid(other).name() + std::to_string(this->targets.size()) + other.to_string(false); if (matrix_operator::m_ops.find(this->id) == matrix_operator::m_ops.end()) { - auto func = [targets = other.degrees(), other](std::vector dimensions, - std::map> _none) { - std::map dims; + auto func = [targets = other.degrees(), other] + (const std::vector &dimensions, const std::unordered_map> &_none) { + std::unordered_map dims; for(auto i = 0; i < dimensions.size(); ++i) dims[targets[i]] = dimensions[i]; return other.to_matrix(dims, std::move(_none)); @@ -124,8 +144,8 @@ matrix_operator& matrix_operator::operator=(matrix_operator &&other) { // evaluations matrix_2 matrix_operator::to_matrix( - std::map &dimensions, - std::map> parameters) const { + std::unordered_map &dimensions, + const std::unordered_map> ¶meters) const { auto it = matrix_operator::m_ops.find(this->id); if (it == matrix_operator::m_ops.end()) throw std::range_error("unable to find operator"); @@ -156,9 +176,9 @@ matrix_2 matrix_operator::to_matrix( std::string matrix_operator::to_string(bool include_degrees) const { if (!include_degrees) return this->id; else if (this->targets.size() == 0) return this->id + "()"; - auto it = this->targets.begin(); + auto it = this->targets.cbegin(); std::string str = this->id + "(" + std::to_string(*it++); - while (it != this->targets.end()) + while (it != this->targets.cend()) str += ", " + std::to_string(*it++); return str + ")"; } @@ -171,35 +191,23 @@ bool matrix_operator::operator==(const matrix_operator &other) const { // predefined operators -// multiplicative identity -matrix_operator matrix_operator::one(int degree) { - std::string op_id = "identity"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); +operator_sum matrix_operator::empty() { + return operator_handler::empty(); +} - // Build up the identity matrix. - for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = 1.0 + 0.0j; - } - return mat; - }; - matrix_operator::define(op_id, {-1}, std::move(func)); - } - return matrix_operator(op_id, {degree}); +product_operator matrix_operator::identity() { + return operator_handler::identity(); } product_operator matrix_operator::identity(int degree) { - return product_operator(std::move(matrix_operator::one(degree))); + return product_operator(matrix_operator(degree)); } product_operator matrix_operator::annihilate(int degree) { std::string op_id = "annihilate"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -216,8 +224,8 @@ product_operator matrix_operator::annihilate(int degree) { product_operator matrix_operator::create(int degree) { std::string op_id = "create"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i + 1 < dimension; i++) { @@ -234,8 +242,8 @@ product_operator matrix_operator::create(int degree) { product_operator matrix_operator::position(int degree) { std::string op_id = "position"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // position = 0.5 * (create + annihilate) @@ -256,8 +264,8 @@ product_operator matrix_operator::position(int degree) { product_operator matrix_operator::momentum(int degree) { std::string op_id = "momentum"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); // momentum = 0.5j * (create - annihilate) @@ -278,8 +286,8 @@ product_operator matrix_operator::momentum(int degree) { product_operator matrix_operator::number(int degree) { std::string op_id = "number"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -296,8 +304,8 @@ product_operator matrix_operator::number(int degree) { product_operator matrix_operator::parity(int degree) { std::string op_id = "parity"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> _none) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; auto mat = matrix_2(dimension, dimension); for (std::size_t i = 0; i < dimension; i++) { @@ -314,8 +322,8 @@ product_operator matrix_operator::parity(int degree) { product_operator matrix_operator::displace(int degree) { std::string op_id = "displace"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> parameters) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> ¶meters) { std::size_t dimension = dimensions[0]; auto entry = parameters.find("displacement"); if (entry == parameters.end()) @@ -341,8 +349,8 @@ product_operator matrix_operator::displace(int degree) { product_operator matrix_operator::squeeze(int degree) { std::string op_id = "squeeze"; if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { - auto func = [](std::vector dimensions, - std::map> parameters) { + auto func = [](const std::vector &dimensions, + const std::unordered_map> ¶meters) { std::size_t dimension = dimensions[0]; auto entry = parameters.find("squeezing"); if (entry == parameters.end()) diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index f8c89d5194..24280f0862 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include #include "cudaq/utils/tensor.h" @@ -20,11 +20,11 @@ namespace cudaq { template class product_operator; -class matrix_operator : operator_handler{ +class matrix_operator : public operator_handler{ private: - static std::map m_ops; + static std::unordered_map m_ops; protected: @@ -35,6 +35,9 @@ class matrix_operator : operator_handler{ matrix_operator(std::string operator_id, std::vector &°rees); public: +#if !defined(NDEBUG) + static bool can_be_canonicalized; // needs to be false; no canonical order can be defined for matrix operator expressions +#endif // tools for custom operators @@ -83,10 +86,10 @@ class matrix_operator : operator_handler{ /// order. virtual std::vector degrees() const; - virtual bool is_identity() const; - // constructors and destructors + matrix_operator(int target); + template, bool> = true> matrix_operator(const T &other); @@ -116,8 +119,8 @@ class matrix_operator : operator_handler{ /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map &dimensions, - std::map> parameters = {}) const; + virtual matrix_2 to_matrix(std::unordered_map &dimensions, + const std::unordered_map> ¶meters = {}) const; virtual std::string to_string(bool include_degrees) const; @@ -129,8 +132,9 @@ class matrix_operator : operator_handler{ // predefined operators - // multiplicative identity - static matrix_operator one(int degree); + static operator_sum empty(); + static product_operator identity(); + static product_operator identity(int degree); static product_operator annihilate(int degree); static product_operator create(int degree); diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h index e8b08cf2ed..bd34730d50 100644 --- a/runtime/cudaq/dynamics/operator_leafs.h +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -62,16 +63,15 @@ class scalar_operator { /// @brief Return the scalar operator as a concrete complex value. std::complex - evaluate(const std::map> parameters = {}) const; + evaluate(const std::unordered_map> ¶meters = {}) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::map dimensions = {}, - const std::map> parameters = {}) const; + matrix_2 to_matrix(const std::unordered_map> ¶meters = {}) const; // comparisons - bool operator==(scalar_operator other); + bool operator==(scalar_operator other) const; // unary operators @@ -126,23 +126,38 @@ class scalar_operator { friend scalar_operator operator-(std::complex other, const scalar_operator &self); }; + +template +class product_operator; + +template +class operator_sum; + class operator_handler { public: +#if !defined(NDEBUG) + static bool can_be_canonicalized; // whether a canonical order can be defined for operator expressions +#endif + virtual ~operator_handler() = default; virtual std::vector degrees() const = 0; - virtual bool is_identity() const = 0; - /// @brief Return the `matrix_operator` as a matrix. /// @arg `dimensions` : A map specifying the number of levels, /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map &dimensions, - std::map> parameters = {}) const = 0; + virtual matrix_2 to_matrix(std::unordered_map &dimensions, + const std::unordered_map> ¶meters = {}) const = 0; virtual std::string to_string(bool include_degrees = true) const = 0; + + template + static operator_sum empty(); + + template...>::value, bool> = true> + static product_operator identity(Args... targets); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 32e52529f0..0824180b21 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -22,6 +22,62 @@ namespace cudaq { // private methods +template +void operator_sum::insert(product_operator &&other) { + // the logic below just ensures that terms are properly combined, if possible + auto it = std::find_if(this->terms.cbegin(), this->terms.cend(), + [&other_ops = static_cast&>(other.operators)](const std::vector &self_ops) { + bool are_same = other_ops.size() == self_ops.size(); + for (auto i = 0; are_same && i < other_ops.size(); ++i) + are_same = other_ops[i] == self_ops[i]; + return are_same; + }); + + if (it == this->terms.end()) { + this->terms.push_back(std::move(other.operators)); + this->coefficients.push_back(std::move(other.coefficient)); + } else { + this->coefficients[std::distance(this->terms.cbegin(), it)] += other.coefficient; + } +} + +template +void operator_sum::aggregate_all() { + std::unordered_map> term_map; + for (auto i = 0; i < this->terms.size(); ++i) { + std::string key; + for (const HandlerTy &op : this->terms[i]) + key += op.to_string(true); // includes degree(s) + term_map[key].push_back(i); + } + + std::vector> terms; + terms.reserve(this->terms.size()); + std::vector coefficients; + coefficients.reserve(this->coefficients.size()); + for (const auto &entry : term_map) { + auto coefficient = std::move(this->coefficients[entry.second[0]]); + for (auto i = 1; i < entry.second.size(); ++i) { + // FIXME: maybe double check equality here if we don't want to rely on to_string too much? + coefficient += this->coefficients[entry.second[i]]; + } + coefficients.push_back(std::move(coefficient)); + terms.push_back(std::move(this->terms[entry.second[0]])); // fine to move + } + this->terms = std::move(terms); + this->coefficients = std::move(coefficients); +} + +template +void operator_sum::aggregate_terms() {} + +template +template +void operator_sum::aggregate_terms(product_operator &&head, Args&& ... args) { + this->insert(std::forward>(head)); + aggregate_terms(std::forward(args)...); +} + template EvaluatedMatrix operator_sum::m_evaluate( MatrixArithmetics arithmetics, bool pad_terms) const { @@ -32,16 +88,17 @@ EvaluatedMatrix operator_sum::m_evaluate( // We need to make sure all matrices are of the same size to sum them up. auto paddedTerm = [&arithmetics, °rees = std::as_const(degrees)](product_operator &&term) { - std::vector prod; - prod.reserve(degrees.size()); + std::vector prod_ops; + prod_ops.reserve(degrees.size()); auto term_degrees = term.degrees(); for (auto degree : degrees) { auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); if (it == term_degrees.end()) - prod.push_back(HandlerTy::one(degree)); + prod_ops.push_back(HandlerTy(degree)); } - prod.insert(prod.end(), std::make_move_iterator(term.operators.begin()), std::make_move_iterator(term.operators.end())); - return product_operator(std::move(term.coefficient), std::move(prod)); + product_operator prod(1, std::move(prod_ops)); + prod *= term; // ensures canonical ordering + return prod; }; if (pad_terms) { @@ -63,24 +120,9 @@ EvaluatedMatrix operator_sum::m_evaluate( } } -template -void operator_sum::aggregate_terms() {} - -template -template -void operator_sum::aggregate_terms(product_operator &&head, Args&& ... args) { - this->terms.push_back(std::move(head.operators)); - this->coefficients.push_back(std::move(head.coefficient)); - aggregate_terms(std::forward(args)...); -} - #define INSTANTIATE_SUM_PRIVATE_METHODS(HandlerTy) \ \ template \ - EvaluatedMatrix operator_sum::m_evaluate( \ - MatrixArithmetics arithmetics, bool pad_terms) const; \ - \ - template \ void operator_sum::aggregate_terms(product_operator &&item2); \ \ template \ @@ -90,7 +132,11 @@ void operator_sum::aggregate_terms(product_operator &&head template \ void operator_sum::aggregate_terms(product_operator &&item1, \ product_operator &&item2, \ - product_operator &&item3); + product_operator &&item3); \ + \ + template \ + EvaluatedMatrix operator_sum::m_evaluate( \ + MatrixArithmetics arithmetics, bool pad_terms) const; INSTANTIATE_SUM_PRIVATE_METHODS(matrix_operator); INSTANTIATE_SUM_PRIVATE_METHODS(spin_operator); @@ -104,12 +150,12 @@ std::vector operator_sum::degrees() const { for (const std::vector &term : this->terms) { for (const HandlerTy &op : term) { auto op_degrees = op.degrees(); - unsorted_degrees.insert(op_degrees.begin(), op_degrees.end()); + unsorted_degrees.insert(op_degrees.cbegin(), op_degrees.cend()); } } - auto degrees = - std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); - return cudaq::detail::canonicalize_degrees(degrees); + auto degrees = std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); + cudaq::detail::canonicalize_degrees(degrees); + return degrees; } template @@ -154,28 +200,16 @@ operator_sum::operator_sum(const product_operator &prod) { template template, Args>...>::value, bool>> operator_sum::operator_sum(Args&&... args) { - this->terms.reserve(sizeof...(Args)); - this->coefficients.reserve(sizeof...(Args)); - aggregate_terms(std::forward&&>(args)...); -} - -template -operator_sum::operator_sum(const std::vector> &terms) { - this->terms.reserve(terms.size()); - this->coefficients.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(term.operators); - this->coefficients.push_back(term.coefficient); - } + this->terms.reserve(sizeof...(Args)); + this->coefficients.reserve(sizeof...(Args)); + aggregate_terms(std::forward&&>(args)...); } template operator_sum::operator_sum(std::vector> &&terms) { - this->terms.reserve(terms.size()); - for (const product_operator& term : terms) { - this->terms.push_back(std::move(term.operators)); - this->coefficients.push_back(std::move(term.coefficient)); - } + this->terms.reserve(terms.size()); + for (auto i = 0; i < terms.size(); ++i) + this->insert(std::move(terms[i])); } template @@ -194,12 +228,11 @@ operator_sum::operator_sum(const operator_sum &other) { template operator_sum::operator_sum(const operator_sum &other) - : coefficients(other.coefficients), terms(other.terms) {} + : coefficients(other.coefficients), terms(other.terms) {} -template -operator_sum::operator_sum(operator_sum &&other) - : coefficients(std::move(other.coefficients)), - terms(std::move(other.terms)) {} +template +operator_sum::operator_sum(operator_sum &&other) + : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} #define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ \ @@ -219,9 +252,6 @@ operator_sum::operator_sum(operator_sum &&other) product_operator &&item3); \ \ template \ - operator_sum::operator_sum(const std::vector> &terms); \ - \ - template \ operator_sum::operator_sum(std::vector> &&terms); \ \ template \ @@ -241,6 +271,38 @@ INSTANTIATE_SUM_CONSTRUCTORS(boson_operator); // assignments +template + template::value && std::is_constructible::value, bool>> +operator_sum& operator_sum::operator=(const product_operator &other) { + this->coefficients.clear(); + this->terms.clear(); + std::vector operators; + operators.reserve(other.operators.size()); + for (const T &op : other.operators) + operators.push_back(op); + this->coefficients.push_back(other.coefficient); + this->terms.push_back(std::move(operators)); + return *this; +} + +template +operator_sum& operator_sum::operator=(product_operator &&other) { + this->coefficients.clear(); + this->terms.clear(); + this->coefficients.push_back(std::move(other.coefficient)); + this->terms.push_back(std::move(other.operators)); + return *this; +} + +template +operator_sum& operator_sum::operator=(const product_operator &other) { + this->coefficients.clear(); + this->terms.clear(); + this->coefficients.push_back(other.coefficient); + this->terms.push_back(other.operators); + return *this; +} + template template::value && std::is_constructible::value, bool>> operator_sum& operator_sum::operator=(const operator_sum &other) { @@ -250,19 +312,18 @@ operator_sum& operator_sum::operator=(const operator_sum operator_sum& operator_sum::operator=(const operator_sum &other) { - if (this != &other) { - coefficients = other.coefficients; - terms = other.terms; - } - return *this; + if (this != &other) { + this->coefficients = other.coefficients; + this->terms = other.terms; + } + return *this; } -template -operator_sum & -operator_sum::operator=(operator_sum &&other) { +template +operator_sum& operator_sum::operator=(operator_sum &&other) { if (this != &other) { - coefficients = std::move(other.coefficients); - terms = std::move(other.terms); + this->coefficients = std::move(other.coefficients); + this->terms = std::move(other.terms); } return *this; } @@ -271,12 +332,24 @@ operator_sum::operator=(operator_sum &&other) { \ template \ operator_sum& operator_sum::operator=( \ - const operator_sum& other); \ + product_operator &&other); \ + \ + template \ + operator_sum& operator_sum::operator=( \ + const product_operator &other); \ + \ + template \ + operator_sum& operator_sum::operator=( \ + const operator_sum &other); \ \ template \ operator_sum& operator_sum::operator=( \ operator_sum &&other); +template +operator_sum& operator_sum::operator=(const product_operator &other); +template +operator_sum& operator_sum::operator=(const product_operator &other); template operator_sum& operator_sum::operator=(const operator_sum &other); template @@ -294,8 +367,8 @@ std::string operator_sum::to_string() const { } template -matrix_2 operator_sum::to_matrix(const std::map &dimensions, - const std::map> ¶meters) const { +matrix_2 operator_sum::to_matrix(std::unordered_map dimensions, + const std::unordered_map> ¶meters) const { return m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); } @@ -306,8 +379,8 @@ matrix_2 operator_sum::to_matrix(const std::map &dimensions \ template \ matrix_2 operator_sum::to_matrix( \ - const std::map &dimensions, \ - const std::map> ¶ms) const; + std::unordered_map dimensions, \ + const std::unordered_map> ¶ms) const; INSTANTIATE_SUM_EVALUATIONS(matrix_operator); INSTANTIATE_SUM_EVALUATIONS(spin_operator); @@ -315,10 +388,13 @@ INSTANTIATE_SUM_EVALUATIONS(boson_operator); // comparisons -template -bool operator_sum::operator==( - const operator_sum &other) const { - throw std::runtime_error("not implemented"); +template +bool operator_sum::operator==(const operator_sum &other) const { + bool are_same = this->terms.size() == other.terms.size(); + for (auto i = 0; are_same && i < this->terms.size(); ++i) + are_same = product_operator(this->coefficients[i], this->terms[i]) == + product_operator(other.coefficients[i], other.terms[i]); + return are_same; } #define INSTANTIATE_SUM_COMPARISONS(HandlerTy) \ @@ -506,19 +582,14 @@ operator_sum operator_sum::operator*(const product_operato template \ operator_sum operator_sum::operator op( \ const product_operator &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ + operator_sum sum; \ + sum.coefficients.reserve(this->coefficients.size() + 1); \ + sum.terms.reserve(this->terms.size() + 1); \ for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - coefficients.push_back(op other.coefficient); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ + sum.coefficients.push_back(coeff); \ for (auto &term : this->terms) \ - terms.push_back(term); \ - terms.push_back(other.operators); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ + sum.terms.push_back(term); \ + sum.insert(op other); \ return sum; \ } @@ -534,34 +605,32 @@ operator_sum operator_sum::operator*(const operator_sum prod(this->coefficients[i], this->terms[i]); prod *= product_operator(other.coefficients[j], other.terms[j]); + // if we do an insert here it can get *very* expensive, hence merge terms at the end sum.coefficients.push_back(std::move(prod.coefficient)); sum.terms.push_back(std::move(prod.operators)); } } + sum.aggregate_all(); return sum; } -#define SUM_ADDITION_SUM(op) \ - template \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + \ - other.coefficients.size()); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_SUM(op) \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const { \ + operator_sum sum; \ + sum.coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + sum.terms.reserve(this->terms.size() + other.terms.size()); \ + for (auto &coeff : this->coefficients) \ + sum.coefficients.push_back(coeff); \ + for (auto &coeff : other.coefficients) \ + sum.coefficients.push_back(op coeff); \ + for (auto &term : this->terms) \ + sum.terms.push_back(term); \ + for (auto &term : other.terms) \ + sum.terms.push_back(term); \ + sum.aggregate_all(); \ + return sum; \ } SUM_ADDITION_SUM(+); @@ -661,8 +730,7 @@ operator_sum& operator_sum::operator*=(const product_opera template \ operator_sum& operator_sum::operator op##=( \ const product_operator &other) { \ - this->coefficients.push_back(op other.coefficient); \ - this->terms.push_back(other.operators); \ + this->insert(op other); \ return *this; \ } @@ -683,11 +751,12 @@ operator_sum::operator*=(const operator_sum &other) { operator_sum& operator_sum::operator op##=( \ const operator_sum &other) { \ this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ + this->terms.reserve(this->terms.size() + other.terms.size()); \ for (auto &coeff : other.coefficients) \ this->coefficients.push_back(op coeff); \ - this->terms.reserve(this->terms.size() + other.terms.size()); \ for (auto &term : other.terms) \ this->terms.push_back(term); \ + this->aggregate_all(); \ return *this; \ } @@ -942,6 +1011,17 @@ INSTANTIATE_SUM_CONVERSION_OPS(*); INSTANTIATE_SUM_CONVERSION_OPS(+); INSTANTIATE_SUM_CONVERSION_OPS(-); +// common operators + +template +operator_sum operator_handler::empty() { + return operator_sum(); +} + +template operator_sum operator_handler::empty(); +template operator_sum operator_handler::empty(); +template operator_sum operator_handler::empty(); + #ifdef CUDAQ_INSTANTIATE_TEMPLATES template class operator_sum; diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index a9dfdd90d9..684be32154 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "cudaq/operators.h" @@ -22,13 +23,64 @@ namespace cudaq { // private methods +#if !defined(NDEBUG) +// check canonicalization by default, individual handlers can set it to false to disable the check +bool operator_handler::can_be_canonicalized = true; + +// returns true if and only if applying the operators in sequence acts only once on each degree of freedom and in canonical order +template +bool product_operator::is_canonicalized() const { + auto canon_degrees = this->degrees(); + std::vector degrees; + degrees.reserve(canon_degrees.size()); + for (const auto &op : this->operators) { + for (auto d : op.degrees()) + degrees.push_back(d); + } + return degrees == canon_degrees; +} +#endif + +template +std::vector::const_iterator product_operator::find_insert_at(const HandlerTy &other) const { + // the logic below just ensures that terms are fully or partially ordered in canonical order - + // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers + return std::find_if(this->operators.crbegin(), this->operators.crend(), + [&other_degrees = static_cast&>(other.degrees())] + (const HandlerTy& self_op) { + const std::vector &self_op_degrees = self_op.degrees(); + for (auto other_degree : other_degrees) { // fixme: special case on single qubit handlers instead? + auto item_it = std::find_if(self_op_degrees.crbegin(), self_op_degrees.crend(), + [other_degree](int self_degree) { return other_degree <= self_degree; }); // FIXME: relies on canonical order + if (item_it != self_op_degrees.crend()) return true; + } + return false; + }).base(); // base causes insert after for reverse iterator +} + +template +template::value && !product_operator::supports_inplace_mult, int>> +void product_operator::insert(T &&other) { + auto pos = this->find_insert_at(other); + this->operators.insert(pos, other); +} + +template +template ::value && product_operator::supports_inplace_mult, bool>> +void product_operator::insert(T &&other) { + auto pos = this->find_insert_at(other); + if (pos != this->operators.begin() && (pos - 1)->target == other.target) + this->coefficient *= this->operators.erase(pos - 1, pos - 1)->inplace_mult(other); // erase: constant time conversion to non-const iterator + else this->operators.insert(pos, std::move(other)); +} + template void product_operator::aggregate_terms() {} template template void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... args) { - this->operators.push_back(head); + this->insert(std::forward(head)); aggregate_terms(std::forward(args)...); } @@ -44,9 +96,8 @@ EvaluatedMatrix product_operator::m_evaluate( std::vector padded; auto op_degrees = op.degrees(); for (const auto °ree : degrees) { - if (std::find(op_degrees.begin(), op_degrees.end(), degree) == op_degrees.end()) { - // FIXME: instead of relying on an identity to exist, replace pad_terms with a function to invoke. - auto identity = HandlerTy::one(degree); + if (std::find(op_degrees.cbegin(), op_degrees.cend(), degree) == op_degrees.cend()) { + auto identity = HandlerTy(degree); padded.push_back(EvaluatedMatrix(identity.degrees(), identity.to_matrix(arithmetics.m_dimensions))); } } @@ -65,7 +116,7 @@ EvaluatedMatrix product_operator::m_evaluate( EvaluatedMatrix prod = padded_op(this->operators[0]); for (auto op_idx = 1; op_idx < this->operators.size(); ++op_idx) { auto op_degrees = this->operators[op_idx].degrees(); - if (op_degrees.size() != 1 || !this->operators[op_idx].is_identity()) + if (op_degrees.size() != 1 || this->operators[op_idx] != HandlerTy(op_degrees[0])) prod = arithmetics.mul(std::move(prod), padded_op(this->operators[op_idx])); } return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); @@ -109,11 +160,11 @@ std::vector product_operator::degrees() const { std::set unsorted_degrees; for (const HandlerTy &term : this->operators) { auto term_degrees = term.degrees(); - unsorted_degrees.insert(term_degrees.begin(), term_degrees.end()); + unsorted_degrees.insert(term_degrees.cbegin(), term_degrees.cend()); } - auto degrees = - std::vector(unsorted_degrees.begin(), unsorted_degrees.end()); - return cudaq::detail::canonicalize_degrees(degrees); + auto degrees = std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); + cudaq::detail::canonicalize_degrees(degrees); + return degrees; } template @@ -151,10 +202,15 @@ INSTANTIATE_PRODUCT_PROPERTIES(boson_operator); // constructors +template +product_operator::product_operator(double coefficient) + : coefficient(coefficient) {} + template product_operator::product_operator(HandlerTy &&atomic) : coefficient(1.) { this->operators.push_back(std::move(atomic)); + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); // relevant for custom matrix operators acting on multiple degrees of freedom } template @@ -163,18 +219,21 @@ product_operator::product_operator(scalar_operator coefficient, Args& : coefficient(std::move(coefficient)) { this->operators.reserve(sizeof...(Args)); aggregate_terms(std::forward(args)...); + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } template product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) : coefficient(std::move(coefficient)){ - this->operators = atomic_operators; + this->operators = atomic_operators; // assumes canonical ordering (if possible) + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } template product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) : coefficient(std::move(coefficient)) { - this->operators = std::move(atomic_operators); + this->operators = std::move(atomic_operators); // assumes canonical ordering (if possible) + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } template @@ -200,6 +259,9 @@ product_operator::product_operator(product_operator &&othe #define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ \ template \ + product_operator::product_operator(double coefficient); \ + \ + template \ product_operator::product_operator(scalar_operator coefficient); \ \ template \ @@ -300,8 +362,8 @@ std::string product_operator::to_string() const { } template -matrix_2 product_operator::to_matrix(std::map dimensions, - std::map> parameters) const { +matrix_2 product_operator::to_matrix(std::unordered_map dimensions, + const std::unordered_map> ¶meters) const { return this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); } @@ -312,8 +374,8 @@ matrix_2 product_operator::to_matrix(std::map dimensions, \ template \ matrix_2 product_operator::to_matrix( \ - std::map dimensions, \ - std::map> parameters) const; + std::unordered_map dimensions, \ + const std::unordered_map> ¶meters) const; INSTANTIATE_PRODUCT_EVALUATIONS(matrix_operator); INSTANTIATE_PRODUCT_EVALUATIONS(spin_operator); @@ -321,10 +383,12 @@ INSTANTIATE_PRODUCT_EVALUATIONS(boson_operator); // comparisons -template -bool product_operator::operator==( - const product_operator &other) const { - throw std::runtime_error("not implemented"); +template +bool product_operator::operator==(const product_operator &other) const { + bool are_same = this->operators.size() == other.operators.size() && this->coefficient == other.coefficient; + for (auto i = 0; are_same && i < this->operators.size(); ++i) + are_same = this->operators[i] == other.operators[i]; + return are_same; } #define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ @@ -477,26 +541,23 @@ operator_sum product_operator::operator*(const operator_su sum.coefficients.push_back(std::move(prod.coefficient)); sum.terms.push_back(std::move(prod.operators)); } + sum.aggregate_all(); return sum; } +// FIXME: potentially unnecessary copy of this #define PRODUCT_ADDITION_SUM(op) \ template \ operator_sum product_operator::operator op( \ const operator_sum &other) const { \ - std::vector coefficients; \ - coefficients.reserve(other.coefficients.size() + 1); \ - coefficients.push_back(this->coefficient); \ + operator_sum sum; \ + sum.coefficients.reserve(other.coefficients.size() + 1); \ + sum.terms.reserve(other.terms.size() + 1); \ for (auto &coeff : other.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(other.terms.size() + 1); \ - terms.push_back(this->operators); \ + sum.coefficients.push_back(op coeff); \ for (auto &term : other.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ + sum.terms.push_back(term); \ + sum.insert(product_operator(*this)); \ return sum; \ } @@ -551,23 +612,8 @@ template product_operator& product_operator::operator*=(const product_operator &other) { this->coefficient *= other.coefficient; this->operators.reserve(this->operators.size() + other.operators.size()); - this->operators.insert(this->operators.end(), other.operators.begin(), other.operators.end()); - return *this; -} - -template <> // specialization for term aggregation -product_operator& product_operator::operator*=(const product_operator &other) { - this->operators.reserve(this->operators.size() + other.operators.size()); // worst case, may not be needed - std::complex spin_factor = 1.0; - for (const spin_operator &op : other.operators) { - auto it = std::find_if(this->operators.rbegin(), this->operators.rend(), - [op_target = op.target] (const spin_operator& self_op) { return self_op.target == op_target; }); - if (it != this->operators.rend()) - spin_factor *= it->in_place_mult(op); - else - this->operators.push_back(op); - } - this->coefficient *= other.coefficient * spin_factor; + for (HandlerTy other_op : other.operators) + this->insert(std::move(other_op)); return *this; } @@ -709,6 +755,22 @@ INSTANTIATE_PRODUCT_CONVERSION_OPS(*, product_operator); INSTANTIATE_PRODUCT_CONVERSION_OPS(+, operator_sum); INSTANTIATE_PRODUCT_CONVERSION_OPS(-, operator_sum); +// common operators + +template...>::value, bool> = true> +product_operator operator_handler::identity(Args... targets) { + static_assert (std::is_constructible_v, "operator handlers must have a constructor that take a single degree of freedom and returns the identity operator on that degree."); + return product_operator(1.0, HandlerTy(targets)...); +} + +template product_operator operator_handler::identity(); +template product_operator operator_handler::identity(); +template product_operator operator_handler::identity(); + +template product_operator operator_handler::identity(int target); +template product_operator operator_handler::identity(int target); +template product_operator operator_handler::identity(int target); + #ifdef CUDAQ_INSTANTIATE_TEMPLATES template class product_operator; diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index d4540e140f..335558ec52 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -50,15 +50,14 @@ scalar_operator& scalar_operator::operator=(scalar_operator &&other) { // evaluations std::complex scalar_operator::evaluate( - const std::map> parameters) const { + const std::unordered_map> ¶meters) const { if (std::holds_alternative(this->value)) return std::get(this->value)(parameters); return std::get>(this->value); } matrix_2 scalar_operator::to_matrix( - const std::map dimensions, - const std::map> parameters) const { + const std::unordered_map> ¶meters) const { auto returnOperator = matrix_2(1, 1); returnOperator[{0, 0}] = evaluate(parameters); return returnOperator; @@ -66,7 +65,7 @@ matrix_2 scalar_operator::to_matrix( // comparison -bool scalar_operator::operator==(scalar_operator other) { +bool scalar_operator::operator==(scalar_operator other) const { if (std::holds_alternative(this->value)) { return std::holds_alternative(other.value) && &std::get(this->value) == &std::get(other.value); @@ -92,7 +91,7 @@ scalar_operator scalar_operator::operator+() const { return *this; } } \ auto newGenerator = \ [other, generator = std::get(this->value)]( \ - std::map> parameters) { \ + const std::unordered_map> ¶meters) { \ return generator(parameters) op other; \ }; \ return scalar_operator(newGenerator); \ @@ -117,10 +116,10 @@ ARITHMETIC_OPERATIONS(-, std::complex); std::get>(other.value)); \ } \ auto newGenerator = \ - [other, *this]( \ - std::map> parameters) { \ - return this->evaluate(parameters) op other.evaluate(parameters); \ - }; \ + [other, *this]( \ + const std::unordered_map> ¶meters) { \ + return this->evaluate(parameters) op other.evaluate(parameters); \ + }; \ return scalar_operator(newGenerator); \ } @@ -137,7 +136,7 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(-); } \ auto newGenerator = \ [other, generator = std::move(std::get(this->value))]( \ - std::map> parameters) { \ + const std::unordered_map> ¶meters) { \ return generator(parameters) op##= other; \ }; \ this->value = newGenerator; \ @@ -164,10 +163,10 @@ ARITHMETIC_OPERATIONS_ASSIGNMENT(-, std::complex); return *this; \ } \ auto newGenerator = \ - [other, *this]( \ - std::map> parameters) { \ - return this->evaluate(parameters) op##= other.evaluate(parameters); \ - }; \ + [other, *this]( \ + const std::unordered_map> ¶meters) { \ + return this->evaluate(parameters) op##= other.evaluate(parameters); \ + }; \ this->value = newGenerator; \ return *this; \ } @@ -201,7 +200,7 @@ ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); } \ auto newGenerator = \ [other, generator = std::get(self.value)]( \ - std::map> parameters) { \ + const std::unordered_map> ¶meters) { \ return other op generator(parameters); \ }; \ return scalar_operator(newGenerator); \ diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 6f9668b697..47fcd63cea 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -7,7 +7,7 @@ ******************************************************************************/ #include -#include +#include #include #include "cudaq/utils/tensor.h" @@ -17,7 +17,7 @@ namespace cudaq { // private helper to optimize arithmetics -std::complex spin_operator::in_place_mult(const spin_operator &other) { +std::complex spin_operator::inplace_mult(const spin_operator &other) { assert(this->target == other.target); // FIXME: make cleaner std::complex factor; if (this->id == 0 || other.id == 0 || this->id == other.id) factor = 1.0; @@ -35,26 +35,24 @@ std::vector spin_operator::degrees() const { // constructors -spin_operator::spin_operator(int op_id, int target) +spin_operator::spin_operator(int target) + : id(0), target(target) {} + +spin_operator::spin_operator(int target, int op_id) : id(op_id), target(target) { assert(0 <= op_id < 4); } -bool spin_operator::is_identity() const { - return this->id == 0; -} - // evaluations -matrix_2 spin_operator::to_matrix(std::map &dimensions, - std::map> parameters) const { +matrix_2 spin_operator::to_matrix(std::unordered_map &dimensions, + const std::unordered_map> ¶meters) const { auto it = dimensions.find(this->target); if (it == dimensions.end()) dimensions[this->target] = 2; else if (it->second != 2) throw std::runtime_error("dimension for spin operator must be 2"); - // FIXME: CHECK CONVENTIONS auto mat = matrix_2(2, 2); if (this->id == 1) { // Z mat[{0, 0}] = 1.0; @@ -90,25 +88,28 @@ bool spin_operator::operator==(const spin_operator &other) const { // defined operators -// multiplicative identity -spin_operator spin_operator::one(int degree) { - return spin_operator(0, degree); +operator_sum spin_operator::empty() { + return operator_handler::empty(); +} + +product_operator spin_operator::identity() { + return operator_handler::identity(); } product_operator spin_operator::i(int degree) { - return product_operator(spin_operator(0, degree)); + return product_operator(spin_operator(degree)); } product_operator spin_operator::z(int degree) { - return product_operator(spin_operator(1, degree)); + return product_operator(spin_operator(degree, 1)); } product_operator spin_operator::x(int degree) { - return product_operator(spin_operator(2, degree)); + return product_operator(spin_operator(degree, 2)); } product_operator spin_operator::y(int degree) { - return product_operator(spin_operator(3, degree)); + return product_operator(spin_operator(degree, 3)); } } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 491706a769..59cde6a218 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -9,7 +9,7 @@ #pragma once #include -#include +#include #include #include "cudaq/utils/tensor.h" @@ -21,7 +21,7 @@ template class product_operator; // FIXME: rename to spin ... -class spin_operator : operator_handler{ +class spin_operator : public operator_handler{ friend class product_operator; private: @@ -30,11 +30,11 @@ friend class product_operator; int id; int target; - spin_operator(int op, int target); + spin_operator(int target, int op_id); // private helper to optimize arithmetics - std::complex in_place_mult(const spin_operator &other); + std::complex inplace_mult(const spin_operator &other); public: @@ -44,13 +44,11 @@ friend class product_operator; /// order. virtual std::vector degrees() const; - virtual bool is_identity() const; - // constructors and destructors - ~spin_operator() = default; + spin_operator(int target); - // assignments + ~spin_operator() = default; // evaluations @@ -59,8 +57,8 @@ friend class product_operator; /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::map &dimensions, - std::map> parameters = {}) const; + virtual matrix_2 to_matrix(std::unordered_map &dimensions, + const std::unordered_map> ¶meters = {}) const; virtual std::string to_string(bool include_degrees) const; @@ -70,8 +68,9 @@ friend class product_operator; // defined operators - // multiplicative identity - static spin_operator one(int degree); + static operator_sum empty(); + static product_operator identity(); + static product_operator i(int degree); static product_operator z(int degree); static product_operator x(int degree); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index e1bd9d37d7..e411b714d9 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,7 +8,7 @@ #pragma once -#include +#include #include #include "utils/tensor.h" @@ -31,13 +31,18 @@ template friend class product_operator; private: - EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + // inserts a new term combining it with an existing one if possible + void insert(product_operator &&other); + + void aggregate_all(); void aggregate_terms(); template void aggregate_terms(product_operator &&head, Args&& ... args); + EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + protected: std::vector> terms; @@ -46,8 +51,6 @@ template friend class product_operator; template, Args>...>::value, bool> = true> operator_sum(Args&&... args); - operator_sum(const std::vector> &terms); - operator_sum(std::vector> &&terms); public: @@ -65,7 +68,7 @@ template friend class product_operator; // constructors and destructors - operator_sum(const product_operator& prod); + operator_sum(const product_operator &other); template::value && std::is_constructible::value, bool> = true> operator_sum(const operator_sum &other); @@ -80,6 +83,13 @@ template friend class product_operator; // assignments + template::value && std::is_constructible::value, bool> = true> + operator_sum& operator=(const product_operator &other); + + operator_sum& operator=(const product_operator &other); + + operator_sum& operator=(product_operator &&other); + template::value && std::is_constructible::value, bool> = true> operator_sum& operator=(const operator_sum &other); @@ -101,9 +111,8 @@ template friend class product_operator; /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 to_matrix( - const std::map &dimensions = {}, - const std::map> ¶meters = {}) const; + matrix_2 to_matrix(std::unordered_map dimensions = {}, + const std::unordered_map> ¶meters = {}) const; // comparisons @@ -217,6 +226,11 @@ template friend class product_operator; template friend operator_sum operator-(const T &other, const product_operator &self); */ + + // common operators + + template + friend operator_sum operator_handler::empty(); }; /// @brief Represents an operator expression consisting of a product of @@ -229,6 +243,26 @@ template friend class product_operator; template friend class operator_sum; private: + // template defined as long as T implements an in-place multiplication - + // won't work if the in-place multiplication was inherited from a base class + template ::value, bool> = true> + static std::true_type handler_mult(int); + template + static std::false_type handler_mult(...); // ellipsis ensures the template above is picked if it exists + static constexpr bool supports_inplace_mult = std::is_same(0)), std::true_type>::value; + +#if !defined(NDEBUG) + bool is_canonicalized() const; +#endif + + std::vector::const_iterator find_insert_at(const HandlerTy &other) const; + + template::value && !product_operator::supports_inplace_mult, int> = 0> + void insert(T &&other); + + template ::value && product_operator::supports_inplace_mult, bool> = true> + void insert(T &&other); + void aggregate_terms(); template @@ -244,8 +278,10 @@ template friend class operator_sum; template...>::value, bool> = true> product_operator(scalar_operator coefficient, Args&&... args); + // keep this constructor protected (otherwise it needs to ensure canonical order) product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + // keep this constructor protected (otherwise it needs to ensure canonical order) product_operator(scalar_operator coefficient, std::vector &&atomic_operators); public: @@ -266,6 +302,8 @@ template friend class operator_sum; // constructors and destructors + product_operator(double coefficient); + product_operator(HandlerTy &&atomic); template::value && std::is_constructible::value, bool> = true> @@ -303,9 +341,8 @@ template friend class operator_sum; /// degrees of freedom: `{0:2, 1:2}`. /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. - matrix_2 - to_matrix(std::map dimensions = {}, - std::map> parameters = {}) const; + matrix_2 to_matrix(std::unordered_map dimensions = {}, + const std::unordered_map> ¶meters = {}) const; // comparisons @@ -384,6 +421,11 @@ template friend class operator_sum; template friend operator_sum operator-(const T &other, const product_operator &self); */ + + // common operators + + template...>::value, bool>> + friend product_operator operator_handler::identity(Args... targets); }; #ifndef CUDAQ_INSTANTIATE_TEMPLATES diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 078c4252b8..9f8c4832dd 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -118,16 +118,16 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { TEST(OperatorExpressions, checkCustomMatrixOps) { auto level_count = 2; - std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; + std::unordered_map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; { - auto func0 = [](std::vector dimensions, - std::map> _none) { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), utils::position_matrix(dimensions[1]));; }; - auto func1 = [](std::vector dimensions, - std::map> _none) { + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::create_matrix(dimensions[0]), utils::number_matrix(dimensions[1]));; }; @@ -245,8 +245,11 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { TEST(OperatorExpressions, checkMatrixOpsWithScalars) { - auto function = [](std::map> parameters) { - return parameters["value"]; + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; }; /// Keeping these fixed for these more simple tests. @@ -371,10 +374,6 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto product = self * other; auto reverse = other * self; - auto create = cudaq::matrix_operator::create(0).get_terms()[0]; - utils::assert_product_equal(product, other.evaluate(), {create}); - utils::assert_product_equal(reverse, other.evaluate(), {create}); - std::vector want_degrees = {0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -394,7 +393,7 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { /// Keeping this fixed throughout. int level_count = 3; - std::map dimensions = {{0, level_count}, {1, level_count}}; + std::unordered_map dimensions = {{0, level_count}, {1, level_count}}; // Addition, same DOF. { @@ -665,16 +664,16 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { auto op1 = cudaq::matrix_operator::create(2); auto op2 = cudaq::matrix_operator::annihilate(0); - std::map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; { - auto func0 = [](std::vector dimensions, - std::map> _none) { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), utils::position_matrix(dimensions[1]));; }; - auto func1 = [](std::vector dimensions, - std::map> _none) { + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::create_matrix(dimensions[0]), utils::number_matrix(dimensions[1]));; }; @@ -702,8 +701,8 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { TEST(OperatorExpressions, checkMatrixOpsParameterVerification) { - std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto squeeze = cudaq::matrix_operator::squeeze(1); auto displace = cudaq::matrix_operator::displace(0); diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index 20809616d6..7bf4e4532f 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -12,8 +12,8 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { - std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto matrix_elementary = cudaq::matrix_operator::parity(1); auto matrix_elementary_expected = utils::parity_matrix(2); @@ -24,9 +24,9 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { auto checkSumEquals = [dimensions, parameters]( cudaq::operator_sum sum, - cudaq::matrix_2 expected) { + cudaq::matrix_2 expected, int expected_num_terms = 2) { auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(sum.num_terms() == expected_num_terms); utils::checkEqual(got, expected); }; @@ -42,9 +42,9 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { // `elementary + elementary` { - checkSumEquals(matrix_elementary + matrix_elementary, matrix_elementary_expected + matrix_elementary_expected); - checkSumEquals(spin_elementary + spin_elementary, spin_elementary_expected + spin_elementary_expected); - checkSumEquals(boson_elementary + boson_elementary, boson_elementary_expected + boson_elementary_expected); + checkSumEquals(matrix_elementary + matrix_elementary, matrix_elementary_expected + matrix_elementary_expected, 1); + checkSumEquals(spin_elementary + spin_elementary, spin_elementary_expected + spin_elementary_expected, 1); + checkSumEquals(boson_elementary + boson_elementary, boson_elementary_expected + boson_elementary_expected, 1); checkSumEquals(matrix_elementary + spin_elementary, matrix_elementary_expected + spin_elementary_expected); checkSumEquals(spin_elementary + matrix_elementary, matrix_elementary_expected + spin_elementary_expected); checkSumEquals(matrix_elementary + boson_elementary, matrix_elementary_expected + boson_elementary_expected); @@ -55,9 +55,9 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { // `elementary - elementary` { - checkSumEquals(matrix_elementary - matrix_elementary, matrix_elementary_expected - matrix_elementary_expected); - checkSumEquals(spin_elementary - spin_elementary, spin_elementary_expected - spin_elementary_expected); - checkSumEquals(boson_elementary - boson_elementary, boson_elementary_expected - boson_elementary_expected); + checkSumEquals(matrix_elementary - matrix_elementary, matrix_elementary_expected - matrix_elementary_expected, 1); + checkSumEquals(spin_elementary - spin_elementary, spin_elementary_expected - spin_elementary_expected, 1); + checkSumEquals(boson_elementary - boson_elementary, boson_elementary_expected - boson_elementary_expected, 1); checkSumEquals(matrix_elementary - spin_elementary, matrix_elementary_expected - spin_elementary_expected); checkSumEquals(spin_elementary - matrix_elementary, spin_elementary_expected - matrix_elementary_expected); checkSumEquals(matrix_elementary - boson_elementary, matrix_elementary_expected - boson_elementary_expected); @@ -105,8 +105,8 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { TEST(OperatorExpressions, checkProductOperatorConversions) { - std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto matrix_product = cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); auto matrix_product_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); auto spin_product = cudaq::spin_operator::y(1) * cudaq::spin_operator::x(0); @@ -116,9 +116,9 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { auto checkSumEquals = [dimensions, parameters]( cudaq::operator_sum sum, - cudaq::matrix_2 expected) { + cudaq::matrix_2 expected, int expected_num_terms = 2) { auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(sum.num_terms() == expected_num_terms); utils::checkEqual(got, expected); }; @@ -134,9 +134,9 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { // `product + product` { - checkSumEquals(matrix_product + matrix_product, matrix_product_expected + matrix_product_expected); - checkSumEquals(spin_product + spin_product, spin_product_expected + spin_product_expected); - checkSumEquals(boson_product + boson_product, boson_product_expected + boson_product_expected); + checkSumEquals(matrix_product + matrix_product, matrix_product_expected + matrix_product_expected, 1); + checkSumEquals(spin_product + spin_product, spin_product_expected + spin_product_expected, 1); + checkSumEquals(boson_product + boson_product, boson_product_expected + boson_product_expected, 1); checkSumEquals(matrix_product + spin_product, matrix_product_expected + spin_product_expected); checkSumEquals(spin_product + matrix_product, matrix_product_expected + spin_product_expected); checkSumEquals(matrix_product + boson_product, matrix_product_expected + boson_product_expected); @@ -147,9 +147,9 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { // `product - product` { - checkSumEquals(matrix_product - matrix_product, matrix_product_expected - matrix_product_expected); - checkSumEquals(spin_product - spin_product, spin_product_expected - spin_product_expected); - checkSumEquals(boson_product - boson_product, boson_product_expected - boson_product_expected); + checkSumEquals(matrix_product - matrix_product, matrix_product_expected - matrix_product_expected, 1); + checkSumEquals(spin_product - spin_product, spin_product_expected - spin_product_expected, 1); + checkSumEquals(boson_product - boson_product, boson_product_expected - boson_product_expected, 1); checkSumEquals(matrix_product - spin_product, matrix_product_expected - spin_product_expected); checkSumEquals(spin_product - matrix_product, spin_product_expected - matrix_product_expected); checkSumEquals(matrix_product - boson_product, matrix_product_expected - boson_product_expected); @@ -197,8 +197,8 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { TEST(OperatorExpressions, checkOperatorSumConversions) { - std::map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto matrix_product = cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); auto matrix_product_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); @@ -253,9 +253,9 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { // `sum + sum` { - checkSumEquals(matrix_sum + matrix_sum, matrix_sum_expected + matrix_sum_expected); - checkSumEquals(spin_sum + spin_sum, spin_sum_expected + spin_sum_expected); - checkSumEquals(boson_sum + boson_sum, boson_sum_expected + boson_sum_expected); + checkSumEquals(matrix_sum + matrix_sum, matrix_sum_expected + matrix_sum_expected, 2); + checkSumEquals(spin_sum + spin_sum, spin_sum_expected + spin_sum_expected, 2); + checkSumEquals(boson_sum + boson_sum, boson_sum_expected + boson_sum_expected, 2); checkSumEquals(matrix_sum + spin_sum, matrix_sum_expected + spin_sum_expected); checkSumEquals(spin_sum + matrix_sum, matrix_sum_expected + spin_sum_expected); checkSumEquals(matrix_sum + boson_sum, matrix_sum_expected + boson_sum_expected); @@ -292,9 +292,9 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { // `sum - sum` { - checkSumEquals(matrix_sum - matrix_sum, matrix_sum_expected - matrix_sum_expected); - checkSumEquals(spin_sum - spin_sum, spin_sum_expected - spin_sum_expected); - checkSumEquals(boson_sum - boson_sum, boson_sum_expected - boson_sum_expected); + checkSumEquals(matrix_sum - matrix_sum, matrix_sum_expected - matrix_sum_expected, 2); + checkSumEquals(spin_sum - spin_sum, spin_sum_expected - spin_sum_expected, 2); + checkSumEquals(boson_sum - boson_sum, boson_sum_expected - boson_sum_expected, 2); checkSumEquals(matrix_sum - spin_sum, matrix_sum_expected - spin_sum_expected); checkSumEquals(spin_sum - matrix_sum, spin_sum_expected - matrix_sum_expected); checkSumEquals(matrix_sum - boson_sum, matrix_sum_expected - boson_sum_expected); @@ -329,11 +329,12 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { checkSumEquals(boson_product * spin_sum, boson_product_expected * spin_sum_expected, 2); } + // `sum * sum` { - checkSumEquals(matrix_sum * matrix_sum, matrix_sum_expected * matrix_sum_expected); - checkSumEquals(spin_sum * spin_sum, spin_sum_expected * spin_sum_expected); - checkSumEquals(boson_sum * boson_sum, boson_sum_expected * boson_sum_expected); + checkSumEquals(matrix_sum * matrix_sum, matrix_sum_expected * matrix_sum_expected, 3); + checkSumEquals(spin_sum * spin_sum, spin_sum_expected * spin_sum_expected, 3); + checkSumEquals(boson_sum * boson_sum, boson_sum_expected * boson_sum_expected, 3); checkSumEquals(matrix_sum * spin_sum, matrix_sum_expected * spin_sum_expected); checkSumEquals(spin_sum * matrix_sum, spin_sum_expected * matrix_sum_expected); checkSumEquals(matrix_sum * boson_sum, matrix_sum_expected * boson_sum_expected); @@ -369,15 +370,15 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { { auto matrix_sum_0 = matrix_sum; matrix_sum_0 += matrix_sum; - checkSumEquals(matrix_sum_0, matrix_sum_expected + matrix_sum_expected); + checkSumEquals(matrix_sum_0, matrix_sum_expected + matrix_sum_expected, 2); auto spin_sum_0 = spin_sum; spin_sum_0 += spin_sum; - checkSumEquals(spin_sum_0, spin_sum_expected + spin_sum_expected); + checkSumEquals(spin_sum_0, spin_sum_expected + spin_sum_expected, 2); auto boson_sum_0 = boson_sum; boson_sum_0 += boson_sum; - checkSumEquals(boson_sum_0, boson_sum_expected + boson_sum_expected); + checkSumEquals(boson_sum_0, boson_sum_expected + boson_sum_expected, 2); matrix_sum_0 = matrix_sum; matrix_sum_0 += spin_sum; @@ -415,15 +416,15 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { { auto matrix_sum_0 = matrix_sum; matrix_sum_0 -= matrix_sum; - checkSumEquals(matrix_sum_0, matrix_sum_expected - matrix_sum_expected); + checkSumEquals(matrix_sum_0, matrix_sum_expected - matrix_sum_expected, 2); auto spin_sum_0 = spin_sum; spin_sum_0 -= spin_sum; - checkSumEquals(spin_sum_0, spin_sum_expected - spin_sum_expected); + checkSumEquals(spin_sum_0, spin_sum_expected - spin_sum_expected, 2); auto boson_sum_0 = boson_sum; boson_sum_0 -= boson_sum; - checkSumEquals(boson_sum_0, boson_sum_expected - boson_sum_expected); + checkSumEquals(boson_sum_0, boson_sum_expected - boson_sum_expected, 2); matrix_sum_0 = matrix_sum; matrix_sum_0 -= spin_sum; @@ -461,15 +462,15 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { { auto matrix_sum_0 = matrix_sum; matrix_sum_0 *= matrix_sum; - checkSumEquals(matrix_sum_0, matrix_sum_expected * matrix_sum_expected); + checkSumEquals(matrix_sum_0, matrix_sum_expected * matrix_sum_expected, 3); auto spin_sum_0 = spin_sum; spin_sum_0 *= spin_sum; - checkSumEquals(spin_sum_0, spin_sum_expected * spin_sum_expected); + checkSumEquals(spin_sum_0, spin_sum_expected * spin_sum_expected, 3); auto boson_sum_0 = boson_sum; boson_sum_0 *= boson_sum; - checkSumEquals(boson_sum_0, boson_sum_expected * boson_sum_expected); + checkSumEquals(boson_sum_0, boson_sum_expected * boson_sum_expected, 3); matrix_sum_0 = matrix_sum; matrix_sum_0 *= spin_sum; diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 15e48d4850..86740b1e1b 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -19,13 +19,6 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - auto local_variable = true; - auto function = [&](std::map> parameters) { - if (!local_variable) - throw std::runtime_error("Local variable not detected."); - return parameters["value"]; - }; - { // Same degrees of freedom. { @@ -143,7 +136,7 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { std::vector want_degrees = {2, 0}; auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - std::map dimensions = {{0, 2},{1, 2},{2, 2}}; + std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; ASSERT_TRUE(spin_sum.degrees() == want_degrees); utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); @@ -182,6 +175,13 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { // Scalar Ops against Elementary Ops { + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; + // matrix operator against constant { auto op = cudaq::matrix_operator::annihilate(0); @@ -225,12 +225,12 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { std::vector want_degrees = {1}; auto op_matrix = utils::annihilate_matrix(2); - auto scalar_matrix = scalar_op.evaluate() * utils::id_matrix(2); + auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); } // spin operator against constant from lambda @@ -242,12 +242,12 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { std::vector want_degrees = {1}; auto op_matrix = utils::PauliX_matrix(); - auto scalar_matrix = scalar_op.evaluate() * utils::id_matrix(2); + auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); } } } @@ -1283,16 +1283,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { TEST(OperatorExpressions, checkCustomOperatorSum) { auto level_count = 2; - std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; + std::unordered_map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; { - auto func0 = [](std::vector dimensions, - std::map> _none) { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), utils::position_matrix(dimensions[1]));; }; - auto func1 = [](std::vector dimensions, - std::map> _none) { + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::create_matrix(dimensions[0]), utils::number_matrix(dimensions[1]));; }; diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index f17dd9baf3..fb0a1f04ff 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -21,13 +21,6 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - auto local_variable = true; - auto function = [&](std::map> parameters) { - if (!local_variable) - throw std::runtime_error("Local variable not detected."); - return parameters["value"]; - }; - { // Same degrees of freedom. { @@ -147,7 +140,7 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { std::vector want_degrees = {2, 0}; auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - std::map dimensions = {{0, 2},{1, 2},{2, 2}}; + std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; ASSERT_TRUE(spin_prod.degrees() == want_degrees); utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); @@ -191,6 +184,13 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { // Scalar Ops against Elementary Ops { + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; + // matrix operator against constant { auto op = cudaq::matrix_operator::annihilate(0); @@ -235,8 +235,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_op.evaluate() * op_matrix, product.to_matrix({{1, 2}})); - utils::checkEqual(scalar_op.evaluate() * op_matrix, reverse.to_matrix({{1, 2}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); } // spin operator against constant from lambda @@ -251,8 +251,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_op.evaluate() * op_matrix, product.to_matrix()); - utils::checkEqual(scalar_op.evaluate() * op_matrix, reverse.to_matrix()); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({}, {{"value", 0.3}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({}, {{"value", 0.3}})); } } } @@ -787,7 +787,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; - std::map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::unordered_map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; // `product_operator + product_operator` { @@ -1145,8 +1145,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { int level_count = 3; - std::map dimensions = { - {0, level_count}, {1, level_count}, {2, level_count + 1}}; + std::unordered_map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; // `product_operator + operator_sum` { @@ -1394,16 +1393,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { TEST(OperatorExpressions, checkCustomProductOps) { auto level_count = 2; - std::map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; + std::unordered_map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; { - auto func0 = [](std::vector dimensions, - std::map> _none) { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), utils::position_matrix(dimensions[1]));; }; - auto func1 = [](std::vector dimensions, - std::map> _none) { + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { return cudaq::kronecker(utils::create_matrix(dimensions[0]), utils::number_matrix(dimensions[1]));; }; diff --git a/unittests/dynamics/scalar_operator.cpp b/unittests/dynamics/scalar_operator.cpp index 36155a2ee5..77a3c53cdb 100644 --- a/unittests/dynamics/scalar_operator.cpp +++ b/unittests/dynamics/scalar_operator.cpp @@ -46,11 +46,14 @@ TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { // From a lambda function. { - auto function = [](std::map> parameters) { - return parameters["value"]; + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; }; - std::map> parameter_map; + std::unordered_map> parameter_map; auto operator_0 = cudaq::scalar_operator(function); auto operator_1 = cudaq::scalar_operator(function); @@ -100,11 +103,14 @@ TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { // From a lambda function. { - auto function = [](std::map> parameters) { - return parameters["value"]; + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; }; - std::map> parameter_map; + std::unordered_map> parameter_map; auto operator_0 = cudaq::scalar_operator(function); auto operator_1 = cudaq::scalar_operator(function); @@ -134,11 +140,11 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - auto local_variable = true; - auto function = [&](std::map> parameters) { - if (!local_variable) - throw std::runtime_error("Local variable not detected."); - return parameters["value"]; + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; }; // + : Constant scalar operator. @@ -375,23 +381,22 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - auto local_variable = true; - auto function = [&](std::map> parameters) { - if (!local_variable) - throw std::runtime_error("Local variable not detected."); - return parameters["value"]; + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; }; // I use another function here to make sure that local variables // that may be unique to each ScalarOp's generators are both kept // track of when we merge the generators. - auto alternative_local_variable = true; - auto alternative_function = - [&](std::map> parameters) { - if (!alternative_local_variable) - throw std::runtime_error("Local variable not detected."); - return parameters["other"]; - }; + auto alternative_function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("other"); + if (entry == parameters.end()) + throw std::runtime_error("other not defined in parameters"); + return entry->second; + }; // + : Constant scalar operator. { @@ -417,7 +422,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op + scalar_op; auto reverse_order_op = scalar_op + other_scalar_op; - std::map> parameter_map = { + std::unordered_map> parameter_map = { {"value", value_1}, {"other", value_0}}; auto got_value = new_scalar_op.evaluate(parameter_map); @@ -451,7 +456,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op - scalar_op; auto reverse_order_op = scalar_op - other_scalar_op; - std::map> parameter_map = { + std::unordered_map> parameter_map = { {"value", value_1}, {"other", value_3}}; auto got_value = new_scalar_op.evaluate(parameter_map); @@ -486,7 +491,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op * scalar_op; auto reverse_order_op = scalar_op * other_scalar_op; - std::map> parameter_map = { + std::unordered_map> parameter_map = { {"value", value_1}, {"other", value_3}}; auto got_value = new_scalar_op.evaluate(parameter_map); @@ -521,7 +526,7 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { auto new_scalar_op = other_scalar_op / scalar_op; auto reverse_order_op = scalar_op / other_scalar_op; - std::map> parameter_map = { + std::unordered_map> parameter_map = { {"value", value_0}, {"other", value_3}}; auto got_value = new_scalar_op.evaluate(parameter_map); diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp index 6c4d7afe75..1a811785f4 100644 --- a/unittests/dynamics/spin_operator.cpp +++ b/unittests/dynamics/spin_operator.cpp @@ -121,8 +121,11 @@ TEST(OperatorExpressions, checkSpinOpsWithComplex) { TEST(OperatorExpressions, checkSpinOpsWithScalars) { - auto function = [](std::map> parameters) { - return parameters["value"]; + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; }; /// Keeping these fixed for these more simple tests. @@ -370,8 +373,8 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { auto got = self + operator_sum; auto reverse = operator_sum + self; - ASSERT_TRUE(got.num_terms() == 3); - ASSERT_TRUE(reverse.num_terms() == 3); + ASSERT_TRUE(got.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); auto self_full = cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); @@ -475,7 +478,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { cudaq::spin_operator::i(1); operator_sum -= cudaq::spin_operator::x(0); - ASSERT_TRUE(operator_sum.num_terms() == 3); + ASSERT_TRUE(operator_sum.num_terms() == 2); auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); From a70b22c6210fea4240c6803da9a8f165a6521a45 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 13 Feb 2025 17:54:15 +0000 Subject: [PATCH 268/311] perf version 2 with map Signed-off-by: Bettina Heim --- main.cpp | 4 +- runtime/cudaq/dynamics/boson_operators.cpp | 35 +- runtime/cudaq/dynamics/boson_operators.h | 11 +- runtime/cudaq/dynamics/matrix_operators.cpp | 124 +++-- runtime/cudaq/dynamics/matrix_operators.h | 7 + runtime/cudaq/dynamics/operator_leafs.h | 2 + runtime/cudaq/dynamics/operator_sum.cpp | 527 +++++-------------- runtime/cudaq/dynamics/product_operators.cpp | 188 +++---- runtime/cudaq/dynamics/spin_operators.cpp | 44 +- runtime/cudaq/dynamics/spin_operators.h | 11 +- runtime/cudaq/dynamics/templates.h | 35 +- runtime/cudaq/operators.h | 88 ++-- unittests/dynamics/matrix_operator.cpp | 15 +- unittests/dynamics/operator_conversions.cpp | 1 - 14 files changed, 381 insertions(+), 711 deletions(-) diff --git a/main.cpp b/main.cpp index 2ee50a889f..7f16f31fe8 100644 --- a/main.cpp +++ b/main.cpp @@ -293,8 +293,8 @@ int main() { leaf_ops.push_back(cudaq::spin_operator::i(i)); } - auto term_length = 100; - auto nr_terms = 20; + auto term_length = 1000; + auto nr_terms = 200; srand(5); // random number seed std::vector> indices; for (auto i = 0; i < nr_terms; ++i) { diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index fa502a78c9..27dd0af43e 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -16,8 +16,21 @@ namespace cudaq { +// private helpers + +std::string boson_operator::op_code_to_string() const { + if (this->op_code == 1) return "Ad"; + else if (this->op_code == 2) return "A"; + else if (this->op_code == 3) return "AdA"; + else return "I"; +} + // read-only properties +const std::string& boson_operator::unique_id() const { + return this->id; +} + std::vector boson_operator::degrees() const { return {this->target}; } @@ -25,11 +38,12 @@ std::vector boson_operator::degrees() const { // constructors boson_operator::boson_operator(int target) - : id(0), target(target) {} + : op_code(0), target(target), id("I" + std::to_string(target)) {} boson_operator::boson_operator(int target, int op_id) - : id(op_id), target(target) { + : op_code(op_id), target(target) { assert(0 <= op_id < 4); + this->id = this->op_code_to_string() + std::to_string(target); } // evaluations @@ -42,13 +56,13 @@ matrix_2 boson_operator::to_matrix(std::unordered_map &dimensions, auto dim = it->second; auto mat = matrix_2(dim, dim); - if (this->id == 1) { // create + if (this->op_code == 1) { // create for (std::size_t i = 0; i + 1 < dim; i++) mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } else if (this->id == 2) { // annihilate + } else if (this->op_code == 2) { // annihilate for (std::size_t i = 0; i + 1 < dim; i++) mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; - } else if (this->id == 3) { // number + } else if (this->op_code == 3) { // number for (std::size_t i = 0; i < dim; i++) mat[{i, i}] = static_cast(i) + 0.0j; } else { // id @@ -59,19 +73,14 @@ matrix_2 boson_operator::to_matrix(std::unordered_map &dimensions, } std::string boson_operator::to_string(bool include_degrees) const { - std::string op_str; - if (this->id == 1) op_str = "create"; - else if (this->id == 2) op_str = "annihilate"; - else if (this->id == 3) op_str = "number"; - else op_str = "identity"; - if (include_degrees) return op_str + "(" + std::to_string(target) + ")"; - else return op_str; + if (include_degrees) return this->op_code_to_string() + "(" + std::to_string(target) + ")"; + else return this->op_code_to_string(); } // comparisons bool boson_operator::operator==(const boson_operator &other) const { - return this->id == other.id && this->target == other.target; + return this->op_code == other.op_code && this->target == other.target; } // defined operators diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index e6426764b5..b6e53096fa 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -25,16 +25,21 @@ class boson_operator : public operator_handler{ private: - // ... - int id; + // 0 = I, 1 = Ad (create), 2 = A (annihilate), 3 = AdA (number) + int op_code; int target; + std::string id; - boson_operator(int target, int op); + boson_operator(int target, int op_code); + + std::string op_code_to_string() const; public: // read-only properties + virtual const std::string& unique_id() const; + /// @brief The degrees of freedom that the operator acts on in canonical /// order. virtual std::vector degrees() const; diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index be84df493c..a32e33f825 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -26,6 +26,15 @@ bool matrix_operator::can_be_canonicalized = false; std::unordered_map matrix_operator::m_ops = {}; +template +std::string matrix_operator::type_prefix() { + return typeid(T).name(); +} + +// no need to prefix the operator id and op code with the type name for these (same names mean the same thing) +template<> std::string matrix_operator::type_prefix() { return ""; } +template<> std::string matrix_operator::type_prefix() { return ""; } + void matrix_operator::define(std::string operator_id, std::vector expected_dimensions, MatrixCallbackFunction &&create) { auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); @@ -51,6 +60,10 @@ product_operator matrix_operator::instantiate(std::string opera // read-only properties +const std::string& matrix_operator::unique_id() const { + return this->id; +} + std::vector matrix_operator::degrees() const { return this->targets; } @@ -58,8 +71,8 @@ std::vector matrix_operator::degrees() const { // constructors matrix_operator::matrix_operator(int degree) { - std::string op_id = "identity"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "identity"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -71,27 +84,38 @@ matrix_operator::matrix_operator(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, std::move(func)); + matrix_operator::define(op_code, {-1}, std::move(func)); } - this->id = op_id; + this->op_code = op_code; this->targets.push_back(degree); + this->id = "I"; + for (auto t : this->targets) + this->id += std::to_string(t); } matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) - : id(operator_id), targets(degrees) { + : op_code(operator_id), targets(degrees), id(operator_id) { assert(this->targets.size() > 0); + for (auto t : this->targets) + this->id += std::to_string(t); } matrix_operator::matrix_operator(std::string operator_id, std::vector &°rees) - : id(operator_id), targets(std::move(degrees)) { + : op_code(operator_id), targets(std::move(degrees)), id(operator_id) { assert(this->targets.size() > 0); + for (auto t : this->targets) + this->id += std::to_string(t); } template, bool>> matrix_operator::matrix_operator(const T &other) { + std::string type_prefix = matrix_operator::type_prefix(); this->targets = other.degrees(); - this->id = typeid(other).name() + std::to_string(this->targets.size()) + other.to_string(false); - if (matrix_operator::m_ops.find(this->id) == matrix_operator::m_ops.end()) { + this->op_code = type_prefix + other.to_string(false) + std::to_string(this->targets.size()); + this->id = type_prefix + other.unique_id(); + for (auto t : this->targets) + this->id += std::to_string(t); + if (matrix_operator::m_ops.find(this->op_code) == matrix_operator::m_ops.end()) { auto func = [targets = other.degrees(), other] (const std::vector &dimensions, const std::unordered_map> &_none) { std::unordered_map dims; @@ -101,7 +125,7 @@ matrix_operator::matrix_operator(const T &other) { }; // the to_matrix method on the spin op will check the dimensions, so we allow arbitrary here std::vector required_dimensions (this->targets.size(), -1); - matrix_operator::define(this->id, std::move(required_dimensions), func); + matrix_operator::define(this->op_code, std::move(required_dimensions), func); } } @@ -109,16 +133,17 @@ template matrix_operator::matrix_operator(const spin_operator &other); template matrix_operator::matrix_operator(const boson_operator &other); matrix_operator::matrix_operator(const matrix_operator &other) - : targets(other.targets), id(other.id) {} + : targets(other.targets), op_code(other.op_code), id(other.id) {} matrix_operator::matrix_operator(matrix_operator &&other) - : targets(std::move(other.targets)), id(other.id) {} + : targets(std::move(other.targets)), op_code(other.op_code), id(other.id) {} // assignments matrix_operator& matrix_operator::operator=(const matrix_operator& other) { if (this != &other) { this->targets = other.targets; + this->op_code = other.op_code; this->id = other.id; } return *this; @@ -136,7 +161,8 @@ template matrix_operator& matrix_operator::operator=(const boson_operator& other matrix_operator& matrix_operator::operator=(matrix_operator &&other) { if (this != &other) { this->targets = std::move(other.targets); - this->id = other.id; + this->op_code = other.op_code; + this->id = other.id; } return *this; } @@ -146,7 +172,7 @@ matrix_operator& matrix_operator::operator=(matrix_operator &&other) { matrix_2 matrix_operator::to_matrix( std::unordered_map &dimensions, const std::unordered_map> ¶meters) const { - auto it = matrix_operator::m_ops.find(this->id); + auto it = matrix_operator::m_ops.find(this->op_code); if (it == matrix_operator::m_ops.end()) throw std::range_error("unable to find operator"); @@ -174,10 +200,10 @@ matrix_2 matrix_operator::to_matrix( } std::string matrix_operator::to_string(bool include_degrees) const { - if (!include_degrees) return this->id; - else if (this->targets.size() == 0) return this->id + "()"; + if (!include_degrees) return this->op_code; + else if (this->targets.size() == 0) return this->op_code + "()"; auto it = this->targets.cbegin(); - std::string str = this->id + "(" + std::to_string(*it++); + std::string str = this->op_code + "(" + std::to_string(*it++); while (it != this->targets.cend()) str += ", " + std::to_string(*it++); return str + ")"; @@ -186,7 +212,7 @@ std::string matrix_operator::to_string(bool include_degrees) const { // comparisons bool matrix_operator::operator==(const matrix_operator &other) const { - return this->id == other.id && this->targets == other.targets; + return this->op_code == other.op_code && this->targets == other.targets; } // predefined operators @@ -204,8 +230,8 @@ product_operator matrix_operator::identity(int degree) { } product_operator matrix_operator::annihilate(int degree) { - std::string op_id = "annihilate"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "annihilate"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -215,15 +241,15 @@ product_operator matrix_operator::annihilate(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::create(int degree) { - std::string op_id = "create"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "create"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -233,15 +259,15 @@ product_operator matrix_operator::create(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::position(int degree) { - std::string op_id = "position"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "position"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -255,15 +281,15 @@ product_operator matrix_operator::position(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::momentum(int degree) { - std::string op_id = "momentum"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "momentum"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -277,15 +303,15 @@ product_operator matrix_operator::momentum(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::number(int degree) { - std::string op_id = "number"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "number"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -295,15 +321,15 @@ product_operator matrix_operator::number(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::parity(int degree) { - std::string op_id = "parity"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "parity"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> &_none) { std::size_t dimension = dimensions[0]; @@ -313,15 +339,15 @@ product_operator matrix_operator::parity(int degree) { } return mat; }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::displace(int degree) { - std::string op_id = "displace"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "displace"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> ¶meters) { std::size_t dimension = dimensions[0]; @@ -340,15 +366,15 @@ product_operator matrix_operator::displace(int degree) { auto term2 = std::conj(displacement_amplitude) * annihilate; return (term1 - term2).exponential(); }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } product_operator matrix_operator::squeeze(int degree) { - std::string op_id = "squeeze"; - if (matrix_operator::m_ops.find(op_id) == matrix_operator::m_ops.end()) { + std::string op_code = "squeeze"; + if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, const std::unordered_map> ¶meters) { std::size_t dimension = dimensions[0]; @@ -368,9 +394,9 @@ product_operator matrix_operator::squeeze(int degree) { auto difference = 0.5 * (term1 - term2); return difference.exponential(); }; - matrix_operator::define(op_id, {-1}, func); + matrix_operator::define(op_code, {-1}, func); } - auto op = matrix_operator(op_id, {degree}); + auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index 24280f0862..ec46beaceb 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -26,9 +26,14 @@ class matrix_operator : public operator_handler{ static std::unordered_map m_ops; + // used when converting other operators to matrix operators + template + static std::string type_prefix(); + protected: std::vector targets; + std::string op_code; std::string id; matrix_operator(std::string operator_id, const std::vector °rees); @@ -82,6 +87,8 @@ class matrix_operator : public operator_handler{ // read-only properties + virtual const std::string& unique_id() const; + /// @brief The degrees of freedom that the operator acts on in canonical /// order. virtual std::vector degrees() const; diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h index bd34730d50..10b4e5aa72 100644 --- a/runtime/cudaq/dynamics/operator_leafs.h +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -141,6 +141,8 @@ class operator_handler { virtual ~operator_handler() = default; + virtual const std::string& unique_id() const = 0; + virtual std::vector degrees() const = 0; /// @brief Return the `matrix_operator` as a matrix. diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 0824180b21..0ae212069d 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -24,48 +24,17 @@ namespace cudaq { template void operator_sum::insert(product_operator &&other) { - // the logic below just ensures that terms are properly combined, if possible - auto it = std::find_if(this->terms.cbegin(), this->terms.cend(), - [&other_ops = static_cast&>(other.operators)](const std::vector &self_ops) { - bool are_same = other_ops.size() == self_ops.size(); - for (auto i = 0; are_same && i < other_ops.size(); ++i) - are_same = other_ops[i] == self_ops[i]; - return are_same; - }); - - if (it == this->terms.end()) { - this->terms.push_back(std::move(other.operators)); - this->coefficients.push_back(std::move(other.coefficient)); - } else { - this->coefficients[std::distance(this->terms.cbegin(), it)] += other.coefficient; - } + auto term_id = other.term_id; // need to copy string since both the operator and the key need it + auto it = this->tmap.find(term_id); + if (it == this->tmap.end()) this->tmap.insert(std::make_pair(term_id, std::move(other))); + else it->second.coefficient += other.coefficient; } template -void operator_sum::aggregate_all() { - std::unordered_map> term_map; - for (auto i = 0; i < this->terms.size(); ++i) { - std::string key; - for (const HandlerTy &op : this->terms[i]) - key += op.to_string(true); // includes degree(s) - term_map[key].push_back(i); - } - - std::vector> terms; - terms.reserve(this->terms.size()); - std::vector coefficients; - coefficients.reserve(this->coefficients.size()); - for (const auto &entry : term_map) { - auto coefficient = std::move(this->coefficients[entry.second[0]]); - for (auto i = 1; i < entry.second.size(); ++i) { - // FIXME: maybe double check equality here if we don't want to rely on to_string too much? - coefficient += this->coefficients[entry.second[i]]; - } - coefficients.push_back(std::move(coefficient)); - terms.push_back(std::move(this->terms[entry.second[0]])); // fine to move - } - this->terms = std::move(terms); - this->coefficients = std::move(coefficients); +void operator_sum::insert(const product_operator &other) { + auto it = this->tmap.find(other.term_id); + if (it == this->tmap.end()) this->tmap.insert(std::make_pair(other.term_id, other)); + else it->second.coefficient += other.coefficient; } template @@ -91,12 +60,16 @@ EvaluatedMatrix operator_sum::m_evaluate( std::vector prod_ops; prod_ops.reserve(degrees.size()); auto term_degrees = term.degrees(); + std::string term_id = ""; for (auto degree : degrees) { auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); - if (it == term_degrees.end()) - prod_ops.push_back(HandlerTy(degree)); + if (it == term_degrees.end()) { + HandlerTy identity(degree); + term_id += identity.unique_id(); + prod_ops.push_back(std::move(identity)); + } } - product_operator prod(1, std::move(prod_ops)); + product_operator prod(1, std::move(prod_ops), std::move(term_id)); prod *= term; // ensures canonical ordering return prod; }; @@ -147,8 +120,8 @@ INSTANTIATE_SUM_PRIVATE_METHODS(boson_operator); template std::vector operator_sum::degrees() const { std::set unsorted_degrees; - for (const std::vector &term : this->terms) { - for (const HandlerTy &op : term) { + for (const auto &entry : this->tmap) { + for (const HandlerTy &op : entry.second.operators) { auto op_degrees = op.degrees(); unsorted_degrees.insert(op_degrees.cbegin(), op_degrees.cend()); } @@ -160,19 +133,17 @@ std::vector operator_sum::degrees() const { template int operator_sum::num_terms() const { - return this->terms.size(); + return this->tmap.size(); } -template -std::vector> -operator_sum::get_terms() const { +template +std::vector> operator_sum::get_terms() const { std::vector> prods; - prods.reserve(this->terms.size()); - for (size_t i = 0; i < this->terms.size(); ++i) { - prods.push_back( - product_operator(this->coefficients[i], this->terms[i])); + prods.reserve(this->tmap.size()); + for (const auto &entry : this->tmap) { + prods.push_back(entry.second); } - return prods; + return prods; } #define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ \ @@ -180,7 +151,7 @@ operator_sum::get_terms() const { std::vector operator_sum::degrees() const; \ \ template \ - int operator_sum::num_terms() const; \ + int operator_sum::num_terms() const; \ \ template \ std::vector> operator_sum::get_terms() const; @@ -193,46 +164,40 @@ INSTANTIATE_SUM_PROPERTIES(boson_operator); template operator_sum::operator_sum(const product_operator &prod) { - this->coefficients.push_back(prod.coefficient); - this->terms.push_back(prod.operators); + this->insert(prod); } template template, Args>...>::value, bool>> operator_sum::operator_sum(Args&&... args) { - this->terms.reserve(sizeof...(Args)); - this->coefficients.reserve(sizeof...(Args)); + this->tmap.reserve(sizeof...(Args)); aggregate_terms(std::forward&&>(args)...); } template operator_sum::operator_sum(std::vector> &&terms) { - this->terms.reserve(terms.size()); - for (auto i = 0; i < terms.size(); ++i) - this->insert(std::move(terms[i])); + this->tmap.reserve(terms.size()); + for (auto &&term : terms) + this->insert(std::move(term)); } template template::value && std::is_constructible::value, bool>> operator_sum::operator_sum(const operator_sum &other) { - this->coefficients = other.coefficients; - this->terms.reserve(other.terms.size()); - for (const auto &term : other.terms) { - std::vector other_terms; - other_terms.reserve(other.terms.size()); - for (const T &op : term) - other_terms.push_back(op); - this->terms.push_back(std::move(other_terms)); + this->tmap.reserve(other.tmap.size()); + for (const auto &entry : other.tmap) { + product_operator prod(entry.second); + this->insert(std::move(prod)); } } template operator_sum::operator_sum(const operator_sum &other) - : coefficients(other.coefficients), terms(other.terms) {} + : tmap(other.tmap) {} template operator_sum::operator_sum(operator_sum &&other) - : coefficients(std::move(other.coefficients)), terms(std::move(other.terms)) {} + : tmap(std::move(other.tmap)) {} #define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ \ @@ -274,32 +239,23 @@ INSTANTIATE_SUM_CONSTRUCTORS(boson_operator); template template::value && std::is_constructible::value, bool>> operator_sum& operator_sum::operator=(const product_operator &other) { - this->coefficients.clear(); - this->terms.clear(); - std::vector operators; - operators.reserve(other.operators.size()); - for (const T &op : other.operators) - operators.push_back(op); - this->coefficients.push_back(other.coefficient); - this->terms.push_back(std::move(operators)); + this->tmap.clear(); + product_operator prod(other); + this->insert(std::move(prod)); return *this; } template operator_sum& operator_sum::operator=(product_operator &&other) { - this->coefficients.clear(); - this->terms.clear(); - this->coefficients.push_back(std::move(other.coefficient)); - this->terms.push_back(std::move(other.operators)); + this->tmap.clear(); + this->insert(std::move(other)); return *this; } template operator_sum& operator_sum::operator=(const product_operator &other) { - this->coefficients.clear(); - this->terms.clear(); - this->coefficients.push_back(other.coefficient); - this->terms.push_back(other.operators); + this->tmap.clear(); + this->insert(other); return *this; } @@ -313,8 +269,7 @@ operator_sum& operator_sum::operator=(const operator_sum operator_sum& operator_sum::operator=(const operator_sum &other) { if (this != &other) { - this->coefficients = other.coefficients; - this->terms = other.terms; + this->tmap = other.tmap; } return *this; } @@ -322,8 +277,7 @@ operator_sum& operator_sum::operator=(const operator_sum operator_sum& operator_sum::operator=(operator_sum &&other) { if (this != &other) { - this->coefficients = std::move(other.coefficients); - this->terms = std::move(other.terms); + this->tmap = std::move(other.tmap); } return *this; } @@ -379,45 +333,25 @@ matrix_2 operator_sum::to_matrix(std::unordered_map dimensi \ template \ matrix_2 operator_sum::to_matrix( \ - std::unordered_map dimensions, \ + std::unordered_map dimensions, \ const std::unordered_map> ¶ms) const; INSTANTIATE_SUM_EVALUATIONS(matrix_operator); INSTANTIATE_SUM_EVALUATIONS(spin_operator); INSTANTIATE_SUM_EVALUATIONS(boson_operator); -// comparisons +// unary operators -template -bool operator_sum::operator==(const operator_sum &other) const { - bool are_same = this->terms.size() == other.terms.size(); - for (auto i = 0; are_same && i < this->terms.size(); ++i) - are_same = product_operator(this->coefficients[i], this->terms[i]) == - product_operator(other.coefficients[i], other.terms[i]); - return are_same; +template +operator_sum operator_sum::operator-() const & { + return *this * -1.; } -#define INSTANTIATE_SUM_COMPARISONS(HandlerTy) \ - \ - template \ - bool operator_sum::operator==(const operator_sum &other) const; - -INSTANTIATE_SUM_COMPARISONS(matrix_operator); -INSTANTIATE_SUM_COMPARISONS(spin_operator); -INSTANTIATE_SUM_COMPARISONS(boson_operator); - -// unary operators - template -operator_sum operator_sum::operator-() const { - std::vector coefficients; - coefficients.reserve(this->coefficients.size()); - for (auto &coeff : this->coefficients) - coefficients.push_back(-1. * coeff); - operator_sum sum; - sum.coefficients = std::move(coefficients); - sum.terms = this->terms; - return sum; +operator_sum operator_sum::operator-() && { + for (auto &entry : this->tmap) + entry.second.coefficient *= -1; + return *this; } template @@ -428,7 +362,10 @@ operator_sum operator_sum::operator+() const { #define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ \ template \ - operator_sum operator_sum::operator-() const; \ + operator_sum operator_sum::operator-() const &; \ + \ + template \ + operator_sum operator_sum::operator-() &&; \ \ template \ operator_sum operator_sum::operator+() const; @@ -439,42 +376,26 @@ INSTANTIATE_SUM_UNARY_OPS(boson_operator); // right-hand arithmetics -#define SUM_MULTIPLICATION(otherTy) \ - template \ - operator_sum operator_sum::operator*(otherTy other) \ - const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size()); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff *other); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = this->terms; \ - return sum; \ +#define SUM_MULTIPLICATION(otherTy) \ + template \ + operator_sum operator_sum::operator*(otherTy other) const { \ + operator_sum sum; \ + sum.tmap.reserve(this->tmap.size()); \ + for (const auto &entry : this->tmap) \ + sum.insert(other * entry.second); \ + return sum; \ } SUM_MULTIPLICATION(double); SUM_MULTIPLICATION(std::complex); SUM_MULTIPLICATION(const scalar_operator &); -#define SUM_ADDITION(otherTy, op) \ - template \ - operator_sum operator_sum::operator op(otherTy other) \ - const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - coefficients.push_back(op other); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - terms.push_back({}); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION(otherTy, op) \ + template \ + operator_sum operator_sum::operator op(otherTy other) const { \ + operator_sum sum(*this); \ + sum.insert(product_operator(op other)); \ + return sum; \ } SUM_ADDITION(double, +); @@ -484,52 +405,6 @@ SUM_ADDITION(std::complex, -); SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); -/* -template -operator_sum -operator_sum::operator*(const HandlerTy &other) const { - std::vector> terms; - terms.reserve(this->terms.size()); - for (auto &term : this->terms) { - std::vector prod; - prod.reserve(term.size() + 1); - for (auto &op : term) - prod.push_back(op); - prod.push_back(other); - terms.push_back(std::move(prod)); - } - operator_sum sum; - sum.coefficients = this->coefficients; - sum.terms = std::move(terms); - return sum; -} - -#define SUM_ADDITION_HANDLER(op) \ - template \ - operator_sum operator_sum::operator op( \ - const HandlerTy &other) const { \ - std::vector coefficients; \ - coefficients.reserve(this->coefficients.size() + 1); \ - coefficients.push_back(op 1.); \ - for (auto &coeff : this->coefficients) \ - coefficients.push_back(coeff); \ - std::vector> terms; \ - terms.reserve(this->terms.size() + 1); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - terms.push_back(std::move(newTerm)); \ - for (auto &term : this->terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ - } - -SUM_ADDITION_HANDLER(+) -SUM_ADDITION_HANDLER(-) -*/ - #define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ \ template \ @@ -549,16 +424,7 @@ SUM_ADDITION_HANDLER(-) template \ operator_sum operator_sum::operator+(const scalar_operator &other) const; \ template \ - operator_sum operator_sum::operator-(const scalar_operator &other) const; \ - -/* - template \ - operator_sum operator_sum::operator*(const HandlerTy &other) const; \ - template \ - operator_sum operator_sum::operator+(const HandlerTy &other) const; \ - template \ - operator_sum operator_sum::operator-(const HandlerTy &other) const; -*/ + operator_sum operator_sum::operator-(const scalar_operator &other) const; INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); @@ -567,14 +433,9 @@ INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template operator_sum operator_sum::operator*(const product_operator &other) const { operator_sum sum; - sum.coefficients.reserve(this->coefficients.size()); - sum.terms.reserve(this->terms.size()); - for (auto i = 0; i < this->terms.size(); ++i) { - product_operator prod(this->coefficients[i], this->terms[i]); - prod *= other; - sum.coefficients.push_back(std::move(prod.coefficient)); - sum.terms.push_back(std::move(prod.operators)); - } + sum.tmap.reserve(this->tmap.size()); + for (const auto &entry : this->tmap) + sum.insert(entry.second * other); return sum; } @@ -582,13 +443,7 @@ operator_sum operator_sum::operator*(const product_operato template \ operator_sum operator_sum::operator op( \ const product_operator &other) const { \ - operator_sum sum; \ - sum.coefficients.reserve(this->coefficients.size() + 1); \ - sum.terms.reserve(this->terms.size() + 1); \ - for (auto &coeff : this->coefficients) \ - sum.coefficients.push_back(coeff); \ - for (auto &term : this->terms) \ - sum.terms.push_back(term); \ + operator_sum sum(*this); \ sum.insert(op other); \ return sum; \ } @@ -599,18 +454,11 @@ SUM_ADDITION_PRODUCT(-) template operator_sum operator_sum::operator*(const operator_sum &other) const { operator_sum sum; - sum.coefficients.reserve(this->coefficients.size() * other.coefficients.size()); - sum.terms.reserve(this->terms.size() * other.terms.size()); - for (auto i = 0; i < this->terms.size(); ++i) { - for (auto j = 0; j < other.terms.size(); ++j) { - product_operator prod(this->coefficients[i], this->terms[i]); - prod *= product_operator(other.coefficients[j], other.terms[j]); - // if we do an insert here it can get *very* expensive, hence merge terms at the end - sum.coefficients.push_back(std::move(prod.coefficient)); - sum.terms.push_back(std::move(prod.operators)); - } + sum.tmap.reserve(this->tmap.size() * other.tmap.size()); + for (const auto &entry_self : this->tmap) { + for (const auto &entry_other : other.tmap) + sum.insert(entry_self.second * entry_other.second); } - sum.aggregate_all(); return sum; } @@ -619,17 +467,11 @@ operator_sum operator_sum::operator*(const operator_sum operator_sum::operator op( \ const operator_sum &other) const { \ operator_sum sum; \ - sum.coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - sum.terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &coeff : this->coefficients) \ - sum.coefficients.push_back(coeff); \ - for (auto &coeff : other.coefficients) \ - sum.coefficients.push_back(op coeff); \ - for (auto &term : this->terms) \ - sum.terms.push_back(term); \ - for (auto &term : other.terms) \ - sum.terms.push_back(term); \ - sum.aggregate_all(); \ + sum.tmap.reserve(this->tmap.size() + other.tmap.size()); \ + for (const auto &entry : this->tmap) \ + sum.tmap.insert(entry); \ + for (const auto &entry : other.tmap) \ + sum.insert(op entry.second); \ return sum; \ } @@ -661,26 +503,23 @@ INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(boson_operator); -#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - operator_sum &operator_sum::operator*=( \ - otherTy other) { \ - for (auto &coeff : this->coefficients) \ - coeff *= other; \ - return *this; \ +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum& operator_sum::operator*=(otherTy other) { \ + for (auto &entry : this->tmap) \ + entry.second.coefficient *= other; \ + return *this; \ } SUM_MULTIPLICATION_ASSIGNMENT(double); SUM_MULTIPLICATION_ASSIGNMENT(std::complex); SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - otherTy other) { \ - this->coefficients.push_back(op other); \ - this->terms.push_back({}); \ - return *this; \ +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum& operator_sum::operator op##=(otherTy other) { \ + this->insert(product_operator(op other)); \ + return *this; \ } SUM_ADDITION_ASSIGNMENT(double, +); @@ -690,39 +529,13 @@ SUM_ADDITION_ASSIGNMENT(std::complex, -); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); -/* -template -operator_sum & -operator_sum::operator*=(const HandlerTy &other) { - for (auto &term : this->terms) - term.push_back(other); - operator_sum sum; - return *this; -} - -#define SUM_ADDITION_HANDLER_ASSIGNMENT(op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - const HandlerTy &other) { \ - coefficients.push_back(op 1.); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - this->terms.push_back(std::move(newTerm)); \ - return *this; \ - } - -SUM_ADDITION_HANDLER_ASSIGNMENT(+) -SUM_ADDITION_HANDLER_ASSIGNMENT(-) -*/ - template operator_sum& operator_sum::operator*=(const product_operator &other) { - for (auto i = 0; i < this->terms.size(); ++i) { - product_operator prod(this->coefficients[i], this->terms[i]); - prod *= other; - this->coefficients[i] = std::move(prod.coefficient); - this->terms[i] = std::move(prod.operators); - } + operator_sum sum; + sum.tmap.reserve(this->tmap.size()); + for (auto &entry : this->tmap) + sum.insert(entry.second *= other); + *this = std::move(sum); return *this; } @@ -738,11 +551,9 @@ SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template -operator_sum & -operator_sum::operator*=(const operator_sum &other) { - this->coefficients.reserve(this->coefficients.size() * - other.coefficients.size()); - *this = *this * other; // we need to update all coefficients and terms anyway +operator_sum& operator_sum::operator*=(const operator_sum &other) { + this->tmap.reserve(this->tmap.size() * other.tmap.size()); + *this = *this * other; // we need to update all entries anyway return *this; } @@ -750,13 +561,9 @@ operator_sum::operator*=(const operator_sum &other) { template \ operator_sum& operator_sum::operator op##=( \ const operator_sum &other) { \ - this->coefficients.reserve(this->coefficients.size() + other.coefficients.size()); \ - this->terms.reserve(this->terms.size() + other.terms.size()); \ - for (auto &coeff : other.coefficients) \ - this->coefficients.push_back(op coeff); \ - for (auto &term : other.terms) \ - this->terms.push_back(term); \ - this->aggregate_all(); \ + this->tmap.reserve(this->tmap.size() + other.tmap.size()); \ + for (const auto &entry : other.tmap) \ + this->insert(op entry.second); \ return *this; \ } @@ -796,58 +603,39 @@ SUM_ADDITION_SUM_ASSIGNMENT(-); template \ operator_sum& operator_sum::operator+=(const operator_sum &other); -/* - template \ - operator_sum& operator_sum::operator*=(const HandlerTy &other); \ - template \ - operator_sum& operator_sum::operator+=(const HandlerTy &other); \ - template \ - operator_sum& operator_sum::operator-=(const HandlerTy &other); \ - -*/ - INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define SUM_MULTIPLICATION_REVERSE(otherTy) \ - template \ - operator_sum operator*(otherTy other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.coefficients.size()); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(coeff *other); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = self.terms; \ - return sum; \ +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum; \ + sum.tmap.reserve(self.tmap.size()); \ + for (auto entry : self.tmap) { \ + entry.second.coefficient *= other; \ + sum.tmap.insert(entry); \ + } \ + return sum; \ } SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); -#define SUM_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.terms.size() + 1); \ - coefficients.push_back(other); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(self.terms.size() + 1); \ - terms.push_back({}); \ - for (auto &term : self.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum; \ + sum.tmap.reserve(self.tmap.size() + 1); \ + for (auto entry : self.tmap) \ + sum.tmap.insert(std::make_pair(entry.first, op std::move(entry.second))); \ + sum.insert(product_operator(other)); \ + return sum; \ } SUM_ADDITION_REVERSE(double, +); @@ -857,52 +645,6 @@ SUM_ADDITION_REVERSE(std::complex, -); SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); -/* -template -operator_sum operator*(const HandlerTy &other, - const operator_sum &self) { - std::vector> terms; - terms.reserve(self.terms.size()); - for (auto &term : self.terms) { - std::vector prod; - prod.reserve(term.size() + 1); - prod.push_back(other); - for (auto &op : term) - prod.push_back(op); - terms.push_back(std::move(prod)); - } - operator_sum sum; - sum.coefficients = self.coefficients; - sum.terms = std::move(terms); - return sum; -} - -#define SUM_ADDITION_HANDLER_REVERSE(op) \ - template \ - operator_sum operator op(const HandlerTy &other, \ - const operator_sum &self) { \ - std::vector coefficients; \ - coefficients.reserve(self.terms.size() + 1); \ - coefficients.push_back(1.); \ - for (auto &coeff : self.coefficients) \ - coefficients.push_back(op coeff); \ - std::vector> terms; \ - terms.reserve(self.terms.size() + 1); \ - std::vector newTerm; \ - newTerm.push_back(other); \ - terms.push_back(std::move(newTerm)); \ - for (auto &term : self.terms) \ - terms.push_back(term); \ - operator_sum sum; \ - sum.coefficients = std::move(coefficients); \ - sum.terms = std::move(terms); \ - return sum; \ - } - -SUM_ADDITION_HANDLER_REVERSE(+) -SUM_ADDITION_HANDLER_REVERSE(-) -*/ - #define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ \ template \ @@ -922,16 +664,7 @@ SUM_ADDITION_HANDLER_REVERSE(-) template \ operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ template \ - operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ - -/* - template \ - operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ - template \ - operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ - template \ - operator_sum operator-(const HandlerTy &other, const operator_sum &self); -*/ + operator_sum operator-(const scalar_operator &other, const operator_sum &self); INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 684be32154..57676a30c6 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -60,18 +60,27 @@ std::vector::const_iterator product_operator::find_insert_ template template::value && !product_operator::supports_inplace_mult, int>> -void product_operator::insert(T &&other) { +void product_operator::insert(T &&other, bool update_id) { auto pos = this->find_insert_at(other); this->operators.insert(pos, other); + if (update_id) this->update_id(); } template template ::value && product_operator::supports_inplace_mult, bool>> -void product_operator::insert(T &&other) { +void product_operator::insert(T &&other, bool update_id) { auto pos = this->find_insert_at(other); if (pos != this->operators.begin() && (pos - 1)->target == other.target) this->coefficient *= this->operators.erase(pos - 1, pos - 1)->inplace_mult(other); // erase: constant time conversion to non-const iterator else this->operators.insert(pos, std::move(other)); + if (update_id) this->update_id(); +} + +template +void product_operator::update_id() { + this->term_id = ""; + for (const auto &op : this->operators) + this->term_id += op.unique_id(); } template @@ -80,7 +89,7 @@ void product_operator::aggregate_terms() {} template template void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... args) { - this->insert(std::forward(head)); + this->insert(std::forward(head), false); aggregate_terms(std::forward(args)...); } @@ -204,11 +213,11 @@ INSTANTIATE_PRODUCT_PROPERTIES(boson_operator); template product_operator::product_operator(double coefficient) - : coefficient(coefficient) {} + : coefficient(coefficient), term_id() {} template product_operator::product_operator(HandlerTy &&atomic) - : coefficient(1.) { + : coefficient(1.), term_id(atomic.unique_id()) { this->operators.push_back(std::move(atomic)); assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); // relevant for custom matrix operators acting on multiple degrees of freedom } @@ -219,20 +228,21 @@ product_operator::product_operator(scalar_operator coefficient, Args& : coefficient(std::move(coefficient)) { this->operators.reserve(sizeof...(Args)); aggregate_terms(std::forward(args)...); + this->update_id(); assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } +// assumes canonical ordering (if possible) template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) - : coefficient(std::move(coefficient)){ - this->operators = atomic_operators; // assumes canonical ordering (if possible) +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators, const std::string &term_id) + : coefficient(std::move(coefficient)), operators(atomic_operators), term_id(term_id) { assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } +// assumes canonical ordering (if possible) template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) - : coefficient(std::move(coefficient)) { - this->operators = std::move(atomic_operators); // assumes canonical ordering (if possible) +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators, std::string &&term_id) + : coefficient(std::move(coefficient)), operators(std::move(atomic_operators)), term_id(std::move(term_id)) { assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } @@ -240,19 +250,22 @@ template template::value && std::is_constructible::value, bool>> product_operator::product_operator(const product_operator &other) : coefficient(other.coefficient) { - for (const T &op : other.operators) + for (const T &other_op : other.operators) { + HandlerTy op(other_op); this->operators.push_back(op); + this->term_id += op.unique_id(); + } } template product_operator::product_operator(const product_operator &other) - : coefficient(other.coefficient) { + : coefficient(other.coefficient), term_id(other.term_id) { this->operators = other.operators; } template product_operator::product_operator(product_operator &&other) - : coefficient(std::move(other.coefficient)) { + : coefficient(std::move(other.coefficient)), term_id(std::move(other.term_id)) { this->operators = std::move(other.operators); } @@ -283,12 +296,12 @@ product_operator::product_operator(product_operator &&othe HandlerTy &&atomic3); \ \ template \ - product_operator::product_operator( \ - scalar_operator coefficient, const std::vector &atomic_operators); \ + product_operator::product_operator(scalar_operator coefficient, \ + const std::vector &atomic_operators, const std::string &term_id); \ \ template \ - product_operator::product_operator( \ - scalar_operator coefficient, std::vector &&atomic_operators); \ + product_operator::product_operator(scalar_operator coefficient, \ + std::vector &&atomic_operators, std::string &&term_id); \ \ template \ product_operator::product_operator( \ @@ -321,6 +334,7 @@ product_operator& product_operator::operator=(const produc if (this != &other) { this->coefficient = other.coefficient; this->operators = other.operators; + this->term_id = other.term_id; } return *this; } @@ -331,6 +345,7 @@ product_operator::operator=(product_operator &&other) { if (this != &other) { this->coefficient = std::move(other.coefficient); this->operators = std::move(other.operators); + this->term_id = std::move(other.term_id); } return *this; } @@ -358,7 +373,7 @@ INSTANTIATE_PRODUCT_ASSIGNMENTS(boson_operator); template std::string product_operator::to_string() const { - throw std::runtime_error("not implemented"); + return this->term_id; } template @@ -385,10 +400,7 @@ INSTANTIATE_PRODUCT_EVALUATIONS(boson_operator); template bool product_operator::operator==(const product_operator &other) const { - bool are_same = this->operators.size() == other.operators.size() && this->coefficient == other.coefficient; - for (auto i = 0; are_same && i < this->operators.size(); ++i) - are_same = this->operators[i] == other.operators[i]; - return are_same; + return this->term_id == other.term_id && this->coefficient == other.coefficient; } #define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ @@ -404,8 +416,14 @@ INSTANTIATE_PRODUCT_COMPARISONS(boson_operator); // unary operators template -product_operator product_operator::operator-() const { - return product_operator(-1. * this->coefficient, this->operators); +product_operator product_operator::operator-() const & { + return product_operator(-1. * this->coefficient, this->operators, this->term_id); +} + +template +product_operator product_operator::operator-() && { + this->coefficient *= -1.; + return *this; } template @@ -416,7 +434,10 @@ product_operator product_operator::operator+() const { #define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ \ template \ - product_operator product_operator::operator-() const; \ + product_operator product_operator::operator-() const &; \ + \ + template \ + product_operator product_operator::operator-() &&; \ \ template \ product_operator product_operator::operator+() const; @@ -431,7 +452,8 @@ INSTANTIATE_PRODUCT_UNARY_OPS(boson_operator); template \ product_operator product_operator::operator*( \ otherTy other) const { \ - return product_operator(other * this->coefficient, this->operators); \ + return product_operator(other * this->coefficient, \ + this->operators, this->term_id); \ } PRODUCT_MULTIPLICATION(double); @@ -453,30 +475,6 @@ PRODUCT_ADDITION(std::complex, -); PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); -/* -template -product_operator -product_operator::operator*(const HandlerTy &other) const { - std::vector terms; - terms.reserve(this->operators.size() + 1); - for (auto &term : this->operators) - terms.push_back(term); - terms.push_back(other); - return product_operator(this->coefficient, std::move(terms)); -} - -#define PRODUCT_ADDITION_HANDLER(op) \ - template \ - operator_sum product_operator::operator op( \ - const HandlerTy &other) const { \ - return operator_sum(product_operator(op 1., HandlerTy(other)), \ - product_operator(*this)); \ - } - -PRODUCT_ADDITION_HANDLER(+) -PRODUCT_ADDITION_HANDLER(-) -*/ - #define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ \ template \ @@ -496,16 +494,7 @@ PRODUCT_ADDITION_HANDLER(-) template \ operator_sum product_operator::operator+(const scalar_operator &other) const; \ template \ - operator_sum product_operator::operator-(const scalar_operator &other) const; \ - -/* - template \ - product_operator product_operator::operator*(const HandlerTy &other) const; \ - template \ - operator_sum product_operator::operator+(const HandlerTy &other) const; \ - template \ - operator_sum product_operator::operator-(const HandlerTy &other) const; -*/ + operator_sum product_operator::operator-(const scalar_operator &other) const; INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); @@ -518,7 +507,8 @@ product_operator product_operator::operator*( terms.reserve(this->operators.size() + other.operators.size()); for (auto &term : this->operators) terms.push_back(term); - return product_operator(this->coefficient, std::move(terms)) *= other; + product_operator self(this->coefficient, std::move(terms), this->term_id); + return self *= other; } #define PRODUCT_ADDITION_PRODUCT(op) \ @@ -534,30 +524,21 @@ PRODUCT_ADDITION_PRODUCT(-) template operator_sum product_operator::operator*(const operator_sum &other) const { operator_sum sum; - sum.coefficients.reserve(other.coefficients.size()); - sum.terms.reserve(other.terms.size()); - for (auto i = 0; i < other.terms.size(); ++i) { - auto prod = *this * product_operator(other.coefficients[i], other.terms[i]); - sum.coefficients.push_back(std::move(prod.coefficient)); - sum.terms.push_back(std::move(prod.operators)); - } - sum.aggregate_all(); + sum.tmap.reserve(other.tmap.size()); + for (const auto &entry : other.tmap) + sum.insert(*this * entry.second); return sum; } -// FIXME: potentially unnecessary copy of this #define PRODUCT_ADDITION_SUM(op) \ template \ operator_sum product_operator::operator op( \ const operator_sum &other) const { \ operator_sum sum; \ - sum.coefficients.reserve(other.coefficients.size() + 1); \ - sum.terms.reserve(other.terms.size() + 1); \ - for (auto &coeff : other.coefficients) \ - sum.coefficients.push_back(op coeff); \ - for (auto &term : other.terms) \ - sum.terms.push_back(term); \ - sum.insert(product_operator(*this)); \ + sum.tmap.reserve(other.tmap.size() + 1); \ + for (auto entry : other.tmap) /* copy here so pair doesn't copy */ \ + sum.tmap.insert(std::make_pair(entry.first, op entry.second)); \ + sum.insert(*this); \ return sum; \ } @@ -600,20 +581,13 @@ PRODUCT_MULTIPLICATION_ASSIGNMENT(double); PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -/* -template -product_operator& product_operator::operator*=(const HandlerTy &other) { - this->operators.push_back(other); - return *this; -} -*/ - template product_operator& product_operator::operator*=(const product_operator &other) { this->coefficient *= other.coefficient; this->operators.reserve(this->operators.size() + other.operators.size()); for (HandlerTy other_op : other.operators) - this->insert(std::move(other_op)); + this->insert(std::move(other_op), false); + this->update_id(); return *this; } @@ -628,11 +602,6 @@ product_operator& product_operator::operator*=(const produ template \ product_operator& product_operator::operator*=(const product_operator &other); -/* - template \ - product_operator& product_operator::operator*=(const HandlerTy &other); \ -*/ - INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); @@ -643,7 +612,8 @@ INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); template \ product_operator operator*(otherTy other, \ const product_operator &self) { \ - return product_operator(other * self.coefficient, self.operators); \ + return product_operator(other * self.coefficient, \ + self.operators, self.term_id); \ } PRODUCT_MULTIPLICATION_REVERSE(double); @@ -665,29 +635,6 @@ PRODUCT_ADDITION_REVERSE(std::complex, -); PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); -/* -template -product_operator operator*(const HandlerTy &other, - const product_operator &self) { - std::vector terms; - terms.reserve(self.operators.size() + 1); - terms.push_back(other); - for (auto &term : self.operators) - terms.push_back(term); - return product_operator(self.coefficient, std::move(terms)); -} - -#define PRODUCT_ADDITION_HANDLER_REVERSE(op) \ - template \ - operator_sum operator op(const HandlerTy &other, \ - const product_operator &self) { \ - return operator_sum(product_operator(1., HandlerTy(other)), op self); \ - } - -PRODUCT_ADDITION_HANDLER_REVERSE(+) -PRODUCT_ADDITION_HANDLER_REVERSE(-) -*/ - #define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ \ template \ @@ -709,15 +656,6 @@ PRODUCT_ADDITION_HANDLER_REVERSE(-) template \ operator_sum operator-(const scalar_operator &other, const product_operator &self); -/* - template \ - product_operator operator*(const HandlerTy &other, const product_operator &self); \ - template \ - operator_sum operator+(const HandlerTy &other, const product_operator &self); \ - template \ - operator_sum operator-(const HandlerTy &other, const product_operator &self); -*/ - INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(boson_operator); diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 47fcd63cea..1e8f024106 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -15,20 +15,32 @@ namespace cudaq { -// private helper to optimize arithmetics +// private helpers + +std::string spin_operator::op_code_to_string() const { + if (this->op_code == 1) return "Z"; + else if (this->op_code == 2) return "X"; + else if (this->op_code == 3) return "Y"; + else return "I"; +} std::complex spin_operator::inplace_mult(const spin_operator &other) { - assert(this->target == other.target); // FIXME: make cleaner + assert(this->target == other.target); std::complex factor; - if (this->id == 0 || other.id == 0 || this->id == other.id) factor = 1.0; - else if (this->id + 1 == other.id || this->id - 2 == other.id) factor = 1.0j; + if (this->op_code == 0 || other.op_code == 0 || this->op_code == other.op_code) factor = 1.0; + else if (this->op_code + 1 == other.op_code || this->op_code - 2 == other.op_code) factor = 1.0j; else factor = -1.0j; - this->id ^= other.id; + this->op_code ^= other.op_code; + this->id = this->op_code_to_string() + std::to_string(target); return factor; } // read-only properties +const std::string& spin_operator::unique_id() const { + return this->id; +} + std::vector spin_operator::degrees() const { return {this->target}; } @@ -36,11 +48,12 @@ std::vector spin_operator::degrees() const { // constructors spin_operator::spin_operator(int target) - : id(0), target(target) {} + : op_code(0), target(target), id("I" + std::to_string(target)) {} spin_operator::spin_operator(int target, int op_id) - : id(op_id), target(target) { + : op_code(op_id), target(target) { assert(0 <= op_id < 4); + this->id = this->op_code_to_string() + std::to_string(target); } // evaluations @@ -54,13 +67,13 @@ matrix_2 spin_operator::to_matrix(std::unordered_map &dimensions, throw std::runtime_error("dimension for spin operator must be 2"); auto mat = matrix_2(2, 2); - if (this->id == 1) { // Z + if (this->op_code == 1) { // Z mat[{0, 0}] = 1.0; mat[{1, 1}] = -1.0; - } else if (this->id == 2) { // X + } else if (this->op_code == 2) { // X mat[{0, 1}] = 1.0; mat[{1, 0}] = 1.0; - } else if (this->id == 3) { // Y + } else if (this->op_code == 3) { // Y mat[{0, 1}] = -1.0j; mat[{1, 0}] = 1.0j; } else { // I @@ -71,19 +84,14 @@ matrix_2 spin_operator::to_matrix(std::unordered_map &dimensions, } std::string spin_operator::to_string(bool include_degrees) const { - std::string op_str; - if (this->id == 1) op_str = "Z"; - else if (this->id == 2) op_str = "X"; - else if (this->id == 3) op_str = "Y"; - else op_str = "I"; - if (include_degrees) return op_str + "(" + std::to_string(target) + ")"; - else return op_str; + if (include_degrees) return this->op_code_to_string() + "(" + std::to_string(target) + ")"; + else return this->op_code_to_string(); } // comparisons bool spin_operator::operator==(const spin_operator &other) const { - return this->id == other.id && this->target == other.target; + return this->op_code == other.op_code && this->target == other.target; } // defined operators diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 59cde6a218..7c2fea27c3 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -27,12 +27,15 @@ friend class product_operator; private: // I = 0, Z = 1, X = 2, Y = 3 - int id; + int op_code; int target; + std::string id; - spin_operator(int target, int op_id); + spin_operator(int target, int op_code); - // private helper to optimize arithmetics + // private helpers + + std::string op_code_to_string() const; std::complex inplace_mult(const spin_operator &other); @@ -40,6 +43,8 @@ friend class product_operator; // read-only properties + virtual const std::string& unique_id() const; + /// @brief The degrees of freedom that the operator acts on in canonical /// order. virtual std::vector degrees() const; diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index e238c9fa5d..6ed00dac5f 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -47,15 +47,6 @@ operator_sum operator+(const scalar_operator &other, const product_op template operator_sum operator-(const scalar_operator &other, const product_operator &self); -/* -template -product_operator operator*(const HandlerTy &other, const product_operator &self); -template -operator_sum operator+(const HandlerTy &other, const product_operator &self); -template -operator_sum operator-(const HandlerTy &other, const product_operator &self); -*/ - template product_operator operator*(const product_operator &other, const product_operator &self); template @@ -81,14 +72,6 @@ template operator_sum operator+(const scalar_operator &other, const operator_sum &self); template operator_sum operator-(const scalar_operator &other, const operator_sum &self); -/* -template -operator_sum operator*(const HandlerTy &other, const operator_sum &self); -template -operator_sum operator+(const HandlerTy &other, const operator_sum &self); -template -operator_sum operator-(const HandlerTy &other, const operator_sum &self); -*/ template operator_sum operator*(const operator_sum &other, const product_operator &self); @@ -149,23 +132,7 @@ operator_sum operator-(const operator_sum &other, const extern template \ operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ extern template \ - operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ - -/* - extern template \ - product_operator operator*(const HandlerTy &other, const product_operator &self); \ - extern template \ - operator_sum operator+(const HandlerTy &other, const product_operator &self); \ - extern template \ - operator_sum operator-(const HandlerTy &other, const product_operator &self); \ - - extern template \ - operator_sum operator*(const HandlerTy &other, const operator_sum &self); \ - extern template \ - operator_sum operator+(const HandlerTy &other, const operator_sum &self); \ - extern template \ - operator_sum operator-(const HandlerTy &other, const operator_sum &self); -*/ + operator_sum operator-(const scalar_operator &other, const operator_sum &self); EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index e411b714d9..292cd88276 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -20,6 +20,9 @@ namespace cudaq { class MatrixArithmetics; class EvaluatedMatrix; +// fixme: write overloads with rvalue qualifiers +// https://stackoverflow.com/questions/37737798/c-is-it-possible-to-overload-the-unary-minus-operator-of-an-rvalue-reference + /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -33,6 +36,7 @@ template friend class product_operator; // inserts a new term combining it with an existing one if possible void insert(product_operator &&other); + void insert(const product_operator &other); void aggregate_all(); @@ -45,8 +49,7 @@ template friend class product_operator; protected: - std::vector> terms; - std::vector coefficients; + std::unordered_map> tmap; template, Args>...>::value, bool> = true> operator_sum(Args&&... args); @@ -114,22 +117,10 @@ template friend class product_operator; matrix_2 to_matrix(std::unordered_map dimensions = {}, const std::unordered_map> ¶meters = {}) const; - // comparisons - - /// @brief True, if the other value is an operator_sum with - /// equivalent terms, and False otherwise. The equality takes into account - /// that operator addition is commutative, as is the product of two operators - /// if they act on different degrees of freedom. The equality comparison does - /// *not* take commutation relations into account, and does not try to reorder - /// terms `blockwise`; it may hence evaluate to False, even if two operators - /// in reality are the same. If the equality evaluates to True, on the other - /// hand, the operators are guaranteed to represent the same transformation - /// for all arguments. - bool operator==(const operator_sum &other) const; - // unary operators - operator_sum operator-() const; + operator_sum operator-() const &; + operator_sum operator-() &&; operator_sum operator+() const; // right-hand arithmetics @@ -143,11 +134,6 @@ template friend class product_operator; operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - /* - operator_sum operator+(const HandlerTy &other) const; - operator_sum operator-(const HandlerTy &other) const; - operator_sum operator*(const HandlerTy &other) const; - */ operator_sum operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; @@ -164,11 +150,6 @@ template friend class product_operator; operator_sum& operator*=(const scalar_operator &other); operator_sum& operator+=(const scalar_operator &other); operator_sum& operator-=(const scalar_operator &other); - /* - operator_sum& operator*=(const HandlerTy &other); - operator_sum& operator+=(const HandlerTy &other); - operator_sum& operator-=(const HandlerTy &other); - */ operator_sum& operator*=(const product_operator &other); operator_sum& operator+=(const product_operator &other); operator_sum& operator-=(const product_operator &other); @@ -198,14 +179,6 @@ template friend class product_operator; friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); template friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - /* - template - friend operator_sum operator*(const T &other, const operator_sum &self); - template - friend operator_sum operator+(const T &other, const operator_sum &self); - template - friend operator_sum operator-(const T &other, const operator_sum &self); - */ template friend operator_sum operator+(double other, const product_operator &self); @@ -220,13 +193,6 @@ template friend class product_operator; template friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - /* - template - friend operator_sum operator+(const T &other, const product_operator &self); - template - friend operator_sum operator-(const T &other, const product_operator &self); - */ - // common operators template @@ -258,10 +224,12 @@ template friend class operator_sum; std::vector::const_iterator find_insert_at(const HandlerTy &other) const; template::value && !product_operator::supports_inplace_mult, int> = 0> - void insert(T &&other); + void insert(T &&other, bool update_id); template ::value && product_operator::supports_inplace_mult, bool> = true> - void insert(T &&other); + void insert(T &&other, bool update_id); + + void update_id(); void aggregate_terms(); @@ -274,15 +242,16 @@ template friend class operator_sum; std::vector operators; scalar_operator coefficient; + std::string term_id; template...>::value, bool> = true> product_operator(scalar_operator coefficient, Args&&... args); // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + product_operator(scalar_operator coefficient, const std::vector &atomic_operators, const std::string &term_id); // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + product_operator(scalar_operator coefficient, std::vector &&atomic_operators, std::string &&term_id); public: // read-only properties @@ -360,7 +329,8 @@ template friend class operator_sum; // unary operators - product_operator operator-() const; + product_operator operator-() const &; + product_operator operator-() &&; product_operator operator+() const; // right-hand arithmetics @@ -374,11 +344,6 @@ template friend class operator_sum; product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - /* - product_operator operator*(const HandlerTy &other) const; - operator_sum operator+(const HandlerTy &other) const; - operator_sum operator-(const HandlerTy &other) const; - */ product_operator operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const; operator_sum operator-(const product_operator &other) const; @@ -389,7 +354,6 @@ template friend class operator_sum; product_operator& operator*=(double other); product_operator& operator*=(std::complex other); product_operator& operator*=(const scalar_operator &other); - // product_operator& operator*=(const HandlerTy &other); product_operator& operator*=(const product_operator &other); // left-hand arithmetics @@ -402,25 +366,35 @@ template friend class operator_sum; template friend operator_sum operator-(double other, const product_operator &self); template + friend operator_sum operator*(double other, const operator_sum &self); + template + friend operator_sum operator+(double other, const operator_sum &self); + template + friend operator_sum operator-(double other, const operator_sum &self); + template friend product_operator operator*(std::complex other, const product_operator &self); template friend operator_sum operator+(std::complex other, const product_operator &self); template friend operator_sum operator-(std::complex other, const product_operator &self); template + friend operator_sum operator*(std::complex other, const operator_sum &self); + template + friend operator_sum operator+(std::complex other, const operator_sum &self); + template + friend operator_sum operator-(std::complex other, const operator_sum &self); + template friend product_operator operator*(const scalar_operator &other, const product_operator &self); template friend operator_sum operator+(const scalar_operator &other, const product_operator &self); template friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - /* template - friend product_operator operator*(const T &other, const product_operator &self); + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); template - friend operator_sum operator+(const T &other, const product_operator &self); + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); template - friend operator_sum operator-(const T &other, const product_operator &self); - */ + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); // common operators diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 9f8c4832dd..b2de44ceff 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -504,8 +504,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { int level_count = 3; std::complex value = 0.125 + 0.5j; - /// `matrix_operator + operator_sum` and `operator_sum + - /// matrix_operator` + // `matrix_operator + operator_sum` { auto self = cudaq::matrix_operator::annihilate(0); auto operator_sum = cudaq::matrix_operator::create(0) + @@ -532,8 +531,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `matrix_operator - operator_sum` and `operator_sum - - /// matrix_operator` + // `matrix_operator - operator_sum` { auto self = cudaq::matrix_operator::annihilate(0); auto operator_sum = cudaq::matrix_operator::create(0) + @@ -560,8 +558,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `matrix_operator * operator_sum` and `operator_sum * - /// matrix_operator` + // `matrix_operator * operator_sum` { auto self = cudaq::matrix_operator::annihilate(0); auto operator_sum = cudaq::matrix_operator::squeeze(0) + @@ -594,7 +591,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } - /// `operator_sum += matrix_operator` + // `operator_sum += matrix_operator` { auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -615,7 +612,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::checkEqual(want_matrix, got_matrix); } - /// `operator_sum -= matrix_operator` + // `operator_sum -= matrix_operator` { auto operator_sum = cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); @@ -635,7 +632,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::checkEqual(want_matrix, got_matrix); } - /// `operator_sum *= matrix_operator` + // `operator_sum *= matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); auto operator_sum = cudaq::matrix_operator::create(0) + diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index 7bf4e4532f..8a2714c096 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -329,7 +329,6 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { checkSumEquals(boson_product * spin_sum, boson_product_expected * spin_sum_expected, 2); } - // `sum * sum` { checkSumEquals(matrix_sum * matrix_sum, matrix_sum_expected * matrix_sum_expected, 3); From fcdb4c82791ab7870ee4dab3dd31ff687cacaaef Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 14 Feb 2025 01:05:29 +0000 Subject: [PATCH 269/311] Fix linblad test and fix leak Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 2 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 288 ++++++++---------- runtime/nvqir/cudensitymat/cudm_helpers.h | 7 +- runtime/nvqir/cudensitymat/cudm_state.cpp | 2 +- runtime/nvqir/cudensitymat/cudm_state.h | 2 +- unittests/dynamics/test_cudm_time_stepper.cpp | 13 +- 6 files changed, 141 insertions(+), 173 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 48114b22c7..799e1a5ef8 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -38,7 +38,7 @@ evolve_result evolve_single( dims.emplace_back(dim); auto liouvillian = helper.construct_liouvillian( hamiltonian, collapse_operators, dims, {}, false); - std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; + // std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; // Need to pass liouvillian here auto time_stepper = std::make_shared(handle, liouvillian); runge_kutta_integrator &integrator = diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 6abbbc4822..92cbd155e4 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -16,6 +16,15 @@ cudm_helper::cudm_helper(cudensitymatHandle_t handle) : handle(handle) {} cudm_helper::~cudm_helper() { cudaDeviceSynchronize(); + for (auto term : m_operatorTerms) { + cudensitymatDestroyOperatorTerm(term); + } + for (auto op : m_elementaryOperators) { + cudensitymatDestroyElementaryOperator(op); + } + for (auto *buffer : m_deviceBuffers) { + cudaFree(buffer); + } } cudensitymatWrappedScalarCallback_t @@ -207,8 +216,6 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( wrapped_tensor_callback = _wrap_tensor_callback(elem_op); } - // FIXME: leak (need to track this buffer somewhere and delete **after** the - // whole evolve) auto *elementaryMat_d = create_array_gpu(flat_matrix); cudensitymatElementaryOperator_t cudm_elem_op = nullptr; @@ -223,7 +230,8 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( destroy_array_gpu(elementaryMat_d); throw std::runtime_error("Failed to create elementary operator."); } - + m_elementaryOperators.emplace(cudm_elem_op); + m_deviceBuffers.emplace(elementaryMat_d); return cudm_elem_op; } @@ -321,6 +329,7 @@ void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); } +// TODO: fix the signature // c_ops: std::vector cudensitymatOperator_t cudm_helper::compute_lindblad_operator( const std::vector &c_ops, @@ -329,175 +338,126 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( throw std::invalid_argument("Collapse operators cannot be empty."); } - cudensitymatOperator_t lindblad_op; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &lindblad_op)); - - std::vector terms; - std::vector elem_ops; - std::vector> all_degrees; - std::vector> all_action_dual_modalities; - - try { - for (const auto &c_op : c_ops) { - size_t dim = c_op.get_rows(); - - if (dim == 0 || c_op.get_columns() != dim) { - throw std::invalid_argument( - "Collapse operator must be a square matrix."); - } - - matrix_2 L_dagger_op_matrix = matrix_2::adjoint(c_op); - - const std::string L_id = "L_op"; - const std::string L_dagger_id = "L_dagger_op"; - - cudaq::matrix_operator::define( - L_id, {-1}, - [c_op](std::vector dims, - std::map>) { return c_op; }); - - cudaq::matrix_operator::define( - L_dagger_id, {-1}, - [L_dagger_op_matrix](std::vector dims, - std::map>) { - return L_dagger_op_matrix; - }); + cudensitymatOperator_t liouvillian; - matrix_operator L_op(L_id, {0}); - matrix_operator L_dagger_op(L_dagger_id, {0}); - - cudensitymatElementaryOperator_t L_elem_op = - create_elementary_operator(L_op, {}, mode_extents); - - cudensitymatElementaryOperator_t L_dagger_elem_op = - create_elementary_operator(L_dagger_op, {}, mode_extents); - - if (!L_elem_op || !L_dagger_elem_op) { - throw std::runtime_error("Failed to create elementary operators in " - "compute_lindblad_operator."); - } - - elem_ops.emplace_back(L_elem_op); - all_degrees.emplace_back(L_op.degrees); - - std::vector mod_vec(L_op.degrees.size(), 1); - all_action_dual_modalities.emplace_back(mod_vec); - - elem_ops.emplace_back(L_dagger_elem_op); - all_degrees.emplace_back(L_dagger_op.degrees); - - mod_vec = std::vector(L_dagger_op.degrees.size(), 0); - all_action_dual_modalities.emplace_back(mod_vec); - - // D1 = L * Lt - cudensitymatOperatorTerm_t term_D1; + // Create an empty operator (super-operator) + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &liouvillian)); // the created empty operator (super-operator) + + for (auto &c_op : c_ops) { + + cudensitymatElementaryOperator_t LOp, LOpDagger, LdaggerLOp; + auto adjointMat = matrix_2::adjoint(c_op); + auto *LOp_d = create_array_gpu(flatten_matrix(c_op)); + auto *LOpDagger_d = create_array_gpu(flatten_matrix(adjointMat)); + auto *LdaggerL_d = create_array_gpu(flatten_matrix(adjointMat * c_op)); + m_deviceBuffers.emplace(LOp_d); + m_deviceBuffers.emplace(LOpDagger_d); + m_deviceBuffers.emplace(LdaggerL_d); + // FIXME: assume degree 0 as we don't have that info here + auto subspace_extents = get_subspace_extents(mode_extents, {0}); + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, 1, subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, + 0, nullptr, CUDA_C_64F, LOp_d, {nullptr, nullptr}, &LOp)); + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, 1, subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, + 0, nullptr, CUDA_C_64F, LOpDagger_d, {nullptr, nullptr}, &LOpDagger)); + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, 1, subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, + 0, nullptr, CUDA_C_64F, LdaggerL_d, {nullptr, nullptr}, &LdaggerLOp)); + m_elementaryOperators.emplace(LOp); + m_elementaryOperators.emplace(LOpDagger); + m_elementaryOperators.emplace(LdaggerLOp); + { + cudensitymatOperatorTerm_t D1_term; + // Create an empty operator term HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term_D1)); - - append_elementary_operator_to_term(term_D1, elem_ops, all_degrees, - all_action_dual_modalities); - - elem_ops.clear(); - all_degrees.clear(); - all_action_dual_modalities.clear(); + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D1_term)); // the created empty operator term + m_operatorTerms.emplace(D1_term); + // Define the operator term + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, D1_term, + 2, // number of elementary tensor operators in the product + std::vector({LOp, LOpDagger}) + .data(), // elementary tensor operators forming the product + std::vector({0, 0}) + .data(), // space modes acted on by the operator product (from + // different sides) + std::vector({0, 1}) + .data(), // space mode action duality (0: from + // the left; 1: from the right) + make_cuDoubleComplex(1.0, 0.0), // default coefficient: Always + // 64-bit-precision complex number + {nullptr, nullptr})); // no time-dependent coefficient associated with + // the operator product - // Add term D1 to the Lindblad operator - cudensitymatWrappedScalarCallback_t scalar_callback = {nullptr, nullptr}; HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, lindblad_op, term_D1, 0, make_cuDoubleComplex(1.0, 0.0), - scalar_callback)); - - // elem_ops.emplace_back(L_dagger_elem_op); - // all_degrees.emplace_back(L_dagger_op.degrees); - - // elem_ops.emplace_back(L_elem_op); - // all_degrees.emplace_back(L_op.degrees); - - // mod_vec = std::vector(L_dagger_op.degrees.size(), 0); - // all_action_dual_modalities.emplace_back(mod_vec); - - // mod_vec = std::vector(L_op.degrees.size(), 0); - // all_action_dual_modalities.emplace_back(mod_vec); - - // // D2 = -0.5 * (Lt * L) - // cudensitymatOperatorTerm_t term_D2; - // HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - // handle, static_cast(mode_extents.size()), - // mode_extents.data(), &term_D2)); - - // append_elementary_operator_to_term(term_D2, elem_ops, all_degrees, - // all_action_dual_modalities); - - // // Clear vectors - // elem_ops.clear(); - // all_degrees.clear(); - // all_action_dual_modalities.clear(); - - // Add term D2 to the Lindblad operator - // HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - // handle, lindblad_op, term_D2, 0, make_cuDoubleComplex(-0.5, 0.0), - // scalar_callback)); - - // elem_ops.emplace_back(L_elem_op); - // all_degrees.emplace_back(L_op.degrees); - - // elem_ops.emplace_back(L_dagger_elem_op); - // all_degrees.emplace_back(L_dagger_op.degrees); - - // mod_vec = std::vector(L_op.degrees.size(), 1); - // all_action_dual_modalities.emplace_back(mod_vec); - - // mod_vec = std::vector(L_dagger_op.degrees.size(), 1); - // all_action_dual_modalities.emplace_back(mod_vec); - - // // D3 = -0.5 * (L * Lt) - // cudensitymatOperatorTerm_t term_D3; - // HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - // handle, static_cast(mode_extents.size()), - // mode_extents.data(), &term_D3)); - // append_elementary_operator_to_term(term_D3, elem_ops, all_degrees, - // all_action_dual_modalities); - - // // Clear vectors - // elem_ops.clear(); - // all_degrees.clear(); - // all_action_dual_modalities.clear(); - - // // Add term D3 to the Lindblad operator - // HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - // handle, lindblad_op, term_D3, 0, make_cuDoubleComplex(-0.5, 0.0), - // scalar_callback)); + handle, liouvillian, + D1_term, // appended operator term + 0, // operator term action duality as a whole (no duality reversing in + // this case) + make_cuDoubleComplex(1, 0.0), // constant coefficient associated with + // the operator term as a whole + {nullptr, + nullptr})); // no time-dependent coefficient associated with the } + { + cudensitymatOperatorTerm_t D2_term; + // Create an empty operator term + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D2_term)); // the created empty operator term + m_operatorTerms.emplace(D2_term); + // Define the operator term + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, D2_term, + 1, // number of elementary tensor operators in the product + std::vector({LdaggerLOp}) + .data(), // elementary tensor operators forming the product + std::vector({0}) + .data(), // space modes acted on by the operator product (from + // different sides) + std::vector({0}).data(), // space mode action duality (0: + // from the left; 1: from the right) + make_cuDoubleComplex(-0.5, 0.0), // default coefficient: Always + // 64-bit-precision complex number + {nullptr, nullptr})); // no time-dependent coefficient associated with + // the operator product - HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); - } catch (const std::exception &e) { - std::cerr << "Exception in compute_lindblad_operator: " << e.what() - << std::endl; - - for (auto term : terms) { - HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - } + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, + D2_term, // appended operator term + 0, // operator term action duality as a whole (no duality reversing in + // this case) + make_cuDoubleComplex(1, 0.0), // constant coefficient associated with + // the operator term as a whole + {nullptr, + nullptr})); // no time-dependent coefficient associated with the - for (auto elem_op : elem_ops) { - HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, + D2_term, // appended operator term + 1, // operator term action duality as a whole (no duality reversing in + // this case) + make_cuDoubleComplex(1, 0.0), // constant coefficient associated with + // the operator term as a whole + {nullptr, + nullptr})); // no time-dependent coefficient associated with the } - - cudensitymatDestroyOperator(lindblad_op); - return nullptr; } - - // for (auto term : terms) { - // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); - // } - - // for (auto elem_op : elem_ops) { - // HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - // } - - return lindblad_op; + // operator term as a whole + std::cout << "Constructed the Liouvillian operator\n"; + return liouvillian; } std::map @@ -533,7 +493,7 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( handle, static_cast(mode_extents.size()), mode_extents.data(), &term)); - + m_operatorTerms.emplace(term); std::vector elem_ops; std::vector> all_degrees; for (const auto &component : product_op.get_terms()) { diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index 15d3133f12..f559401ea0 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -15,7 +15,7 @@ #include #include #include - +#include namespace cudaq { class cudm_helper { public: @@ -93,6 +93,11 @@ class cudm_helper { private: cudensitymatHandle_t handle; + // Things that we create that need to be cleaned up. + // Use a set so that it's safe to push pointer multiple times. + std::unordered_set m_deviceBuffers; + std::unordered_set m_elementaryOperators; + std::unordered_set m_operatorTerms; }; extern template cudensitymatOperator_t diff --git a/runtime/nvqir/cudensitymat/cudm_state.cpp b/runtime/nvqir/cudensitymat/cudm_state.cpp index ac13c96998..d30f4db9d3 100644 --- a/runtime/nvqir/cudensitymat/cudm_state.cpp +++ b/runtime/nvqir/cudensitymat/cudm_state.cpp @@ -158,7 +158,7 @@ cudm_state cudm_state::clone(const cudm_state &other) { } cudm_state::cudm_state(cudensitymatHandle_t handle, - const std::vector> rawData, + const std::vector> &rawData, const std::vector &hilbertSpaceDims) : rawData_(rawData), gpuDataSize_(rawData.size()), state_(nullptr), handle_(handle), hilbertSpaceDims_(hilbertSpaceDims) { diff --git a/runtime/nvqir/cudensitymat/cudm_state.h b/runtime/nvqir/cudensitymat/cudm_state.h index 1eb5ddcf9c..36cf9a5900 100644 --- a/runtime/nvqir/cudensitymat/cudm_state.h +++ b/runtime/nvqir/cudensitymat/cudm_state.h @@ -26,7 +26,7 @@ class cudm_state { public: /// @brief To initialize state with raw data. explicit cudm_state(cudensitymatHandle_t handle, - const std::vector> rawData, + const std::vector> &rawData, const std::vector &hilbertSpaceDims); /// @brief To initialize state from a `cudaq::state` diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 37551778d7..5eb583c22b 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -130,14 +130,13 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { const std::vector dims = {10}; auto input_state = std::make_unique(handle_, initial_state, dims); - // auto c_op_0 = cudaq::matrix_operator::annihilate(0); - auto c_op_0 = cudaq::matrix_operator::create(0); + auto c_op_0 = cudaq::matrix_operator::annihilate(0); auto cudm_lindblad_op = helper_->compute_lindblad_operator({c_op_0.to_matrix({{0, 10}})}, dims); auto time_stepper = std::make_unique(handle_, cudm_lindblad_op); - auto output_state = time_stepper_->compute(*input_state, 0.0, 1.0); + auto output_state = time_stepper->compute(*input_state, 0.0, 1.0); std::cout << "Printing output_state ..." << std::endl; output_state.dumpDeviceData(); @@ -150,8 +149,12 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { helper_->print_complex_vector(output_state_vec); - EXPECT_TRUE(std::abs(output_state_vec[4 * 10 + 4] - - std::complex(1.0, 0.0)) < 1e-12); + EXPECT_NEAR( + std::abs(output_state_vec[4 * 10 + 4] - std::complex(5.0, 0.0)), + 0.0, 1e-12); + EXPECT_NEAR( + std::abs(output_state_vec[5 * 10 + 5] - std::complex(-5.0, 0.0)), + 0.0, 1e-12); HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudm_lindblad_op)); } From 5d6e73e926d669c2e36223c988fae67f27080e0a Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 14 Feb 2025 02:56:43 +0000 Subject: [PATCH 270/311] Add full density matrix evolve and reduce test time Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 6 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 150 ++++++++++-------- runtime/nvqir/cudensitymat/cudm_helpers.h | 7 + runtime/nvqir/cudensitymat/cudm_state.cpp | 9 +- unittests/dynamics/test_evolve_single.cpp | 62 +++++++- 5 files changed, 163 insertions(+), 71 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 799e1a5ef8..b1694b647b 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -36,15 +36,17 @@ evolve_result evolve_single( std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); + + auto cudmState = cudm_state(handle, initial_state, dims); auto liouvillian = helper.construct_liouvillian( - hamiltonian, collapse_operators, dims, {}, false); + hamiltonian, collapse_operators, dims, {}, cudmState.is_density_matrix()); // std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; // Need to pass liouvillian here auto time_stepper = std::make_shared(handle, liouvillian); runge_kutta_integrator &integrator = dynamic_cast(in_integrator); integrator.set_stepper(time_stepper); - integrator.set_state(cudm_state(handle, initial_state, dims)); + integrator.set_state(std::move(cudmState)); // auto integrator = std::make_unique( // cudm_state(handle, initial_state, dims), 0.0, time_stepper, 1); // integrator.set_option("dt", 0.000001); diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 92cbd155e4..9138ffd2fa 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -8,7 +8,7 @@ #include "cudm_helpers.h" #include "cudm_error_handling.h" - +#include "common/Logger.h" using namespace cudaq; namespace cudaq { @@ -471,6 +471,45 @@ cudm_helper::convert_dimensions(const std::vector &mode_extents) { return dimensions; } +std::vector> +cudm_helper::convert_to_cudensitymat( + const operator_sum &op, + const std::vector &mode_extents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + + std::vector> + result; + + for (const auto &product_op : op.get_terms()) { + cudensitymatOperatorTerm_t term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &term)); + m_operatorTerms.emplace(term); + std::vector elem_ops; + std::vector> all_degrees; + for (const auto &component : product_op.get_terms()) { + // No need to check type + // just call to_matrix on it + if (const auto *elem_op = + dynamic_cast(&component)) { + auto cudm_elem_op = + create_elementary_operator(*elem_op, {}, mode_extents); + elem_ops.emplace_back(cudm_elem_op); + all_degrees.emplace_back(elem_op->degrees); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + append_elementary_operator_to_term(term, elem_ops, all_degrees, {}); + result.emplace_back(std::make_pair(product_op.get_coefficient(), term)); + } + return result; +} + template cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( const std::map> ¶meters, @@ -480,70 +519,29 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( throw std::invalid_argument("Operator sum cannot be empty."); } - try { - cudensitymatOperator_t operator_handle; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &operator_handle)); - - // std::vector elementary_operators; - - for (const auto &product_op : op.get_terms()) { - cudensitymatOperatorTerm_t term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), - mode_extents.data(), &term)); - m_operatorTerms.emplace(term); - std::vector elem_ops; - std::vector> all_degrees; - for (const auto &component : product_op.get_terms()) { - // No need to check type - // just call to_matrix on it - if (const auto *elem_op = - dynamic_cast(&component)) { - auto cudm_elem_op = - create_elementary_operator(*elem_op, parameters, mode_extents); - elem_ops.emplace_back(cudm_elem_op); - all_degrees.emplace_back(elem_op->degrees); - } else { - // Catch anything that we don't know - throw std::runtime_error("Unhandled type!"); - } - } - append_elementary_operator_to_term(term, elem_ops, all_degrees, {}); - auto coeff = product_op.get_coefficient(); - cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - - if (!coeff.get_generator()) { - const auto coeffVal = coeff.evaluate(); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, - make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), - wrapped_callback)); - } else { - wrapped_callback = _wrap_callback(coeff); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), - wrapped_callback)); - } + cudensitymatOperator_t operator_handle; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &operator_handle)); - // FIXME: leak - // We must track these handles and destroy **after** evolve finishes - // Destroy the term - // HANDLE_CUDM_ERROR(cudensitymatDestroyOperatorTerm(term)); + for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { + cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - // // Cleanup - // for (auto &elem_op : elementary_operators) { - // HANDLE_CUDM_ERROR(cudensitymatDestroyElementaryOperator(elem_op)); - // } + if (!coeff.get_generator()) { + const auto coeffVal = coeff.evaluate(); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, + make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), + wrapped_callback)); + } else { + wrapped_callback = _wrap_callback(coeff); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), + wrapped_callback)); } - - return operator_handle; - } catch (const std::exception &e) { - std::cerr << "Error in convert_to_cudensitymat_operator: " << e.what() - << std::endl; - throw; } + + return operator_handle; } cudensitymatOperator_t cudm_helper::construct_liouvillian( @@ -554,11 +552,39 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( const std::map> ¶meters, bool is_master_equation) { if (!is_master_equation && collapse_operators.empty()) { + cudaq::info("Construct state vector Liouvillian"); auto liouvillian = op * std::complex(0.0, -1.0); return convert_to_cudensitymat_operator(parameters, liouvillian, mode_extents); } else { - throw std::runtime_error("TODO: handle Lindblad equation"); + cudaq::info("Construct density matrix Liouvillian"); + cudensitymatOperator_t liouvillian; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + handle, static_cast(mode_extents.size()), mode_extents.data(), + &liouvillian)); + // Append an operator term to the operator (super-operator) + for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { + cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; + if (!coeff.get_generator()) { + const auto coeffVal = coeff.evaluate(); + const auto leftCoeff = std::complex(0.0, -1.0) * coeffVal; + // -i constant (left multiplication) + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, term, 0, + make_cuDoubleComplex(leftCoeff.real(), leftCoeff.imag()), + wrapped_callback)); + + // +i constant (right multiplication, i.e., dual) + const auto rightCoeff = std::complex(0.0, 1.0) * coeffVal; + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, term, 1, + make_cuDoubleComplex(rightCoeff.real(), rightCoeff.imag()), + wrapped_callback)); + } else { + throw std::runtime_error("TODO: implement callback"); + } + } + return liouvillian; } } diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index f559401ea0..4db8654220 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -45,6 +45,9 @@ class cudm_helper { const operator_sum &op, const std::vector &mode_extents); + std::vector> + convert_to_cudensitymat(const operator_sum &op, + const std::vector &mode_extents); // Construct Liouvillian cudensitymatOperator_t construct_liouvillian( const operator_sum &op, @@ -60,6 +63,10 @@ class cudm_helper { const std::vector &collapse_operators, double gamma); + // std::vector> compute_lindblad_operator(const std::vector &c_ops, + // const std::vector &mode_extents); + + // Helper Functions std::map convert_dimensions(const std::vector &mode_extents); diff --git a/runtime/nvqir/cudensitymat/cudm_state.cpp b/runtime/nvqir/cudensitymat/cudm_state.cpp index d30f4db9d3..84ed55e243 100644 --- a/runtime/nvqir/cudensitymat/cudm_state.cpp +++ b/runtime/nvqir/cudensitymat/cudm_state.cpp @@ -25,10 +25,9 @@ cudm_state::cudm_state(cudensitymatHandle_t handle, if (!simState.is_on_gpu()) throw std::runtime_error("Unexpected state. This must be a state created " "by the dynamics target"); - const bool isDensityMat = simState.get_tensor().extents.size() == 2; - - gpuDataSize_ = isDensityMat ? calculate_density_matrix_size(hilbertSpaceDims) - : calculate_state_vector_size(hilbertSpaceDims); + const bool isDensityMat = simState.get_tensor().get_num_elements() == + calculate_density_matrix_size(hilbertSpaceDims); + gpuDataSize_ = simState.get_tensor().get_num_elements(); const size_t dataSize = gpuDataSize_ * sizeof(std::complex); HANDLE_CUDA_ERROR(cudaMalloc(reinterpret_cast(&gpuData_), dataSize)); @@ -257,7 +256,7 @@ bool cudm_state::is_density_matrix() const { return false; } - return rawData_.size() == calculate_density_matrix_size(hilbertSpaceDims_); + return gpuDataSize_ == calculate_density_matrix_size(hilbertSpaceDims_); } std::vector> cudm_state::get_raw_data() const { diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 81d87c1923..531cf0cdf2 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -73,6 +73,64 @@ TEST(EvolveTester, checkSimple) { } } +TEST(EvolveTester, checkSimpleDensityMatrix) { + const std::map dims = {{0, 2}}; + const std::string op_id = "pauli_x"; + auto func = [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = cudaq::matrix_2(2, 2); + mat[{1, 0}] = 1.0; + mat[{0, 1}] = 1.0; + return mat; + }; + cudaq::matrix_operator::define(op_id, {-1}, func); + auto ham = cudaq::product_operator( + 2.0 * M_PI * 0.1, cudaq::matrix_operator(op_id, {0})); + constexpr int numSteps = 10; + cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); + + cudaq::matrix_operator::define( + "pauli_z", {-1}, + [](std::vector dimensions, + std::map> _none) { + if (dimensions.size() != 1) + throw std::invalid_argument("Must have a singe dimension"); + if (dimensions[0] != 2) + throw std::invalid_argument("Must have dimension 2"); + auto mat = cudaq::matrix_2(2, 2); + mat[{0, 0}] = 1.0; + mat[{1, 1}] = -1.0; + return mat; + }); + auto pauliZ = cudaq::product_operator( + std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); + auto initialState = + cudaq::state::from_data(std::vector>{1.0, 0.0, 0.0, 0.0}); + + cudaq::runge_kutta_integrator integrator; + integrator.dt = 0.001; + integrator.order = 1; + auto result = cudaq::evolve_single(ham, dims, schedule, initialState, + integrator, {}, {&pauliZ}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + for (const auto &t : schedule) { + const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); + theoryResults.emplace_back(expected); + } + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } +} + TEST(EvolveTester, checkCompositeSystem) { constexpr int cavity_levels = 10; const std::map dims = {{0, 2}, {1, cavity_levels}}; @@ -100,8 +158,8 @@ TEST(EvolveTester, checkCompositeSystem) { auto initialState = cudaq::state::from_data( std::make_pair(initial_state_vec.data(), initial_state_vec.size())); cudaq::runge_kutta_integrator integrator; - integrator.dt = 0.000001; - integrator.order = 1; + integrator.dt = 0.001; + integrator.order = 4; auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, integrator, {}, {&cavity_occ_op, &atom_occ_op}, true); From 0d31c8bb438b53268e399fe413fc75ea25129c7c Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 14 Feb 2025 06:34:16 +0000 Subject: [PATCH 271/311] Support static collapse op and add a test Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 130 +++++++++++++++++- runtime/nvqir/cudensitymat/cudm_helpers.h | 9 +- .../cudensitymat/runge_kutta_integrator.cpp | 2 +- unittests/dynamics/test_evolve_single.cpp | 64 ++++++++- 4 files changed, 192 insertions(+), 13 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 9138ffd2fa..e6a4ea9bb5 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -7,8 +7,8 @@ ******************************************************************************/ #include "cudm_helpers.h" -#include "cudm_error_handling.h" #include "common/Logger.h" +#include "cudm_error_handling.h" using namespace cudaq; namespace cudaq { @@ -329,6 +329,94 @@ void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); } +std::pair +cudm_helper::compute_lindblad_operator_terms( + operator_sum &collapseOp, + const std::vector &mode_extents) { + std::map dimensions; + for (int i = 0; i < mode_extents.size(); ++i) + dimensions[i] = mode_extents[i]; + auto c_op = collapseOp.to_matrix(dimensions); + auto degrees = collapseOp.degrees(); + auto adjointMat = matrix_2::adjoint(c_op); + cudensitymatElementaryOperator_t LOp, LOpDagger, LdaggerLOp; + auto *LOp_d = create_array_gpu(flatten_matrix(c_op)); + auto *LOpDagger_d = create_array_gpu(flatten_matrix(adjointMat)); + auto *LdaggerL_d = create_array_gpu(flatten_matrix(adjointMat * c_op)); + m_deviceBuffers.emplace(LOp_d); + m_deviceBuffers.emplace(LOpDagger_d); + m_deviceBuffers.emplace(LdaggerL_d); + auto subspace_extents = get_subspace_extents(mode_extents, degrees); + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, subspace_extents.size(), subspace_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, LOp_d, + {nullptr, nullptr}, &LOp)); + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, subspace_extents.size(), subspace_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, LOpDagger_d, + {nullptr, nullptr}, &LOpDagger)); + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + handle, subspace_extents.size(), subspace_extents.data(), + CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, LdaggerL_d, + {nullptr, nullptr}, &LdaggerLOp)); + m_elementaryOperators.emplace(LOp); + m_elementaryOperators.emplace(LOpDagger); + m_elementaryOperators.emplace(LdaggerLOp); + + cudensitymatOperatorTerm_t D1_term; + // Create an empty operator term + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D1_term)); // the created empty operator term + m_operatorTerms.emplace(D1_term); + // Define the operator term + std::vector d1Degree; // stacked degrees + d1Degree.insert(d1Degree.end(), degrees.begin(), degrees.end()); + d1Degree.insert(d1Degree.end(), degrees.begin(), degrees.end()); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, D1_term, + 2, // number of elementary tensor operators in the product + std::vector({LOp, LOpDagger}) + .data(), // elementary tensor operators forming the product + d1Degree.data(), // space modes acted on by the operator product (from + // different sides) + std::vector({0, 1}).data(), // space mode action duality (0: from + // the left; 1: from the right) + make_cuDoubleComplex(1.0, 0.0), // default coefficient: Always + // 64-bit-precision complex number + {nullptr, nullptr})); // no time-dependent coefficient associated with + // the operator product + + cudensitymatOperatorTerm_t D2_term; + // Create an empty operator term + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D2_term)); // the created empty operator term + m_operatorTerms.emplace(D2_term); + // Define the operator term + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + handle, D2_term, + 1, // number of elementary tensor operators in the product + std::vector({LdaggerLOp}) + .data(), // elementary tensor operators forming the product + degrees.data(), // space modes acted on by the operator + // product (from different sides) + std::vector({0}).data(), // space mode action duality (0: + // from the left; 1: from the right) + make_cuDoubleComplex(-0.5, 0.0), // default coefficient: Always + // 64-bit-precision complex number + {nullptr, nullptr})); // no time-dependent coefficient associated with + // the operator product + + return std::make_pair(D1_term, D2_term); +} + // TODO: fix the signature // c_ops: std::vector cudensitymatOperator_t cudm_helper::compute_lindblad_operator( @@ -562,7 +650,8 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( HANDLE_CUDM_ERROR(cudensitymatCreateOperator( handle, static_cast(mode_extents.size()), mode_extents.data(), &liouvillian)); - // Append an operator term to the operator (super-operator) + // Append an operator term to the operator (super-operator) + // Handle the Hamiltonian for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (!coeff.get_generator()) { @@ -584,6 +673,43 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( throw std::runtime_error("TODO: implement callback"); } } + + // Handle collapsed operators + for (auto &collapse_operators : collapse_operators) { + auto [d1Term, d2Term] = + compute_lindblad_operator_terms(*collapse_operators, mode_extents); + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, + d1Term, // appended operator term + 0, // operator term action duality as a whole (no duality reversing in + // this case) + make_cuDoubleComplex(1, 0.0), // constant coefficient associated with + // the operator term as a whole + {nullptr, + nullptr})); // no time-dependent coefficient associated with the + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, + d2Term, // appended operator term + 0, // operator term action duality as a whole (no duality reversing in + // this case) + make_cuDoubleComplex(1, 0.0), // constant coefficient associated with + // the operator term as a whole + {nullptr, + nullptr})); // no time-dependent coefficient associated with the + + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, + d2Term, // appended operator term + 1, // operator term action duality as a whole (no duality reversing in + // this case) + make_cuDoubleComplex(1, 0.0), // constant coefficient associated with + // the operator term as a whole + {nullptr, + nullptr})); // no time-dependent coefficient associated with the + } + return liouvillian; } } diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index 4db8654220..8e4a2e3ee5 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -63,10 +63,11 @@ class cudm_helper { const std::vector &collapse_operators, double gamma); - // std::vector> compute_lindblad_operator(const std::vector &c_ops, - // const std::vector &mode_extents); - - + std::pair + compute_lindblad_operator_terms( + operator_sum &collapseOp, + const std::vector &mode_extents); + // Helper Functions std::map convert_dimensions(const std::vector &mode_extents); diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index 159b92902a..1ff33ec2c8 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -91,7 +91,7 @@ void runge_kutta_integrator::integrate(double target_time) { m_t += step_size; } - std::cout << "Integration complete. Final time: " << m_t << std::endl; + // std::cout << "Integration complete. Final time: " << m_t << std::endl; } // TODO: remove this diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 531cf0cdf2..28c12f36fa 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -8,12 +8,12 @@ #include "common/EigenDense.h" #include "cudaq/evolution.h" +#include "cudaq/runge_kutta_integrator.h" +#include "cudm_state.h" #include #include #include #include -#include "cudaq/runge_kutta_integrator.h" -#include "cudm_state.h" TEST(EvolveTester, checkSimple) { const std::map dims = {{0, 2}}; @@ -52,7 +52,7 @@ TEST(EvolveTester, checkSimple) { std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); auto initialState = cudaq::state::from_data(std::vector>{1.0, 0.0}); - + cudaq::runge_kutta_integrator integrator; integrator.dt = 0.001; integrator.order = 1; @@ -108,9 +108,9 @@ TEST(EvolveTester, checkSimpleDensityMatrix) { }); auto pauliZ = cudaq::product_operator( std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); - auto initialState = - cudaq::state::from_data(std::vector>{1.0, 0.0, 0.0, 0.0}); - + auto initialState = cudaq::state::from_data( + std::vector>{1.0, 0.0, 0.0, 0.0}); + cudaq::runge_kutta_integrator integrator; integrator.dt = 0.001; integrator.order = 1; @@ -175,3 +175,55 @@ TEST(EvolveTester, checkCompositeSystem) { EXPECT_NEAR((double)expVals[0] + (double)expVals[1], num_photons, 1e-2); } } + +TEST(EvolveTester, checkCompositeSystemWithCollapse) { + constexpr int cavity_levels = 10; + const std::map dims = {{0, 2}, {1, cavity_levels}}; + auto a = cudaq::matrix_operator::annihilate(1); + auto a_dag = cudaq::matrix_operator::create(1); + + auto sm = cudaq::matrix_operator::annihilate(0); + auto sm_dag = cudaq::matrix_operator::create(0); + + auto atom_occ_op = cudaq::matrix_operator::number(0); + auto cavity_occ_op = cudaq::matrix_operator::number(1); + auto hamiltonian = 2 * M_PI * atom_occ_op + 2 * M_PI * cavity_occ_op + + 2 * M_PI * 0.25 * (sm * a_dag + sm_dag * a); + // auto matrix = hamiltonian.to_matrix(dims); + // std::cout << "Matrix:\n" << matrix.dump() << "\n"; + Eigen::Vector2cd qubit_state; + qubit_state << 1.0, 0.0; + Eigen::VectorXcd cavity_state = Eigen::VectorXcd::Zero(cavity_levels); + const int num_photons = 5; + cavity_state[num_photons] = 1.0; + Eigen::VectorXcd initial_state_vec = + Eigen::kroneckerProduct(cavity_state, qubit_state); + Eigen::MatrixXcd rho0 = initial_state_vec * initial_state_vec.transpose(); + std::cout << "Initial rho:\n" << rho0 << "\n"; + constexpr int num_steps = 11; + const auto timeSteps = cudaq::linspace(0.0, 0.5, num_steps); + cudaq::Schedule schedule(timeSteps); + auto initialState = + cudaq::state::from_data(std::make_pair(rho0.data(), rho0.size())); + cudaq::runge_kutta_integrator integrator; + integrator.dt = 0.001; + integrator.order = 4; + constexpr double decayRate = 0.1; + auto collapsedOp = std::sqrt(decayRate) * a; + auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, + integrator, {&collapsedOp}, + {&cavity_occ_op, &atom_occ_op}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 2); + const double totalParticleCount = expVals[0] + expVals[1]; + const auto time = timeSteps[count++]; + const double expectedResult = num_photons * std::exp(-decayRate * time); + std::cout << "t = " << time << "; particle count = " << totalParticleCount + << " vs " << expectedResult << "\n"; + EXPECT_NEAR(totalParticleCount, expectedResult, 0.1); + } +} \ No newline at end of file From eb5688bdb91dcaf4a355fdecb4d6ba6dc7d09096 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Fri, 14 Feb 2025 14:31:17 -0800 Subject: [PATCH 272/311] * Adding conversion mechanism (to operator_sum) after merging in Bettina's commits * Fixing build errors by including only operators.h in operator_sum and product_operator unittests * Removing helpers.h in runtime/cudaq, as it has been added in runtime/cudaq/dynamics * Removing duplicate template instantiations of product_operator * Formatting Signed-off-by: Sachin Pisal --- main.cpp | 637 ++++++------ runtime/cudaq/base_integrator.h | 51 +- runtime/cudaq/dynamics/CMakeLists.txt | 3 +- runtime/cudaq/dynamics/boson_operators.cpp | 53 +- runtime/cudaq/dynamics/boson_operators.h | 16 +- runtime/cudaq/dynamics/callback.cpp | 47 +- runtime/cudaq/dynamics/callback.h | 61 +- runtime/cudaq/dynamics/helpers.cpp | 78 +- runtime/cudaq/dynamics/helpers.h | 32 +- runtime/cudaq/dynamics/manipulation.cpp | 38 +- runtime/cudaq/dynamics/manipulation.h | 24 +- runtime/cudaq/dynamics/matrix_operators.cpp | 312 +++--- runtime/cudaq/dynamics/matrix_operators.h | 53 +- runtime/cudaq/dynamics/operator_leafs.h | 78 +- runtime/cudaq/dynamics/operator_sum.cpp | 850 ++++++++-------- runtime/cudaq/dynamics/product_operators.cpp | 772 ++++++++------- runtime/cudaq/dynamics/scalar_operators.cpp | 189 ++-- runtime/cudaq/dynamics/spin_operators.cpp | 55 +- runtime/cudaq/dynamics/spin_operators.h | 18 +- runtime/cudaq/dynamics/templates.h | 373 +++---- runtime/cudaq/evolution.h | 3 +- runtime/cudaq/helpers.h | 46 - runtime/cudaq/operators.h | 344 ++++--- runtime/cudaq/runge_kutta_integrator.h | 3 +- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 17 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 40 +- runtime/nvqir/cudensitymat/cudm_helpers.h | 14 +- .../nvqir/cudensitymat/cudm_op_conversion.h | 2 +- runtime/nvqir/cudensitymat/cudm_solver.h | 4 +- runtime/nvqir/cudensitymat/cudm_state.cpp | 4 +- runtime/nvqir/cudensitymat/cudm_state.h | 4 +- .../cudensitymat/runge_kutta_integrator.cpp | 5 +- unittests/CMakeLists.txt | 3 +- unittests/dynamics/matrix_operator.cpp | 286 +++--- unittests/dynamics/operator_conversions.cpp | 620 +++++++----- unittests/dynamics/operator_sum.cpp | 913 +++++++++--------- unittests/dynamics/product_operator.cpp | 816 ++++++++-------- unittests/dynamics/scalar_operator.cpp | 52 +- unittests/dynamics/spin_operator.cpp | 136 ++- unittests/dynamics/test_cudm_helpers.cpp | 17 +- unittests/dynamics/test_evolve_single.cpp | 105 +- unittests/dynamics/test_helpers.cpp | 119 +-- unittests/dynamics/test_mocks.h | 25 +- .../dynamics/test_runge_kutta_integrator.cpp | 36 +- unittests/dynamics/utils.cpp | 18 +- unittests/dynamics/utils.h | 10 +- 46 files changed, 3876 insertions(+), 3506 deletions(-) delete mode 100644 runtime/cudaq/helpers.h diff --git a/main.cpp b/main.cpp index 7f16f31fe8..38362d25e8 100644 --- a/main.cpp +++ b/main.cpp @@ -14,321 +14,324 @@ int main() { - // multiplication inplace with itself - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = cudaq::spin_operator::identity(); - - int nr_reps = 1000; - std::cout << "multiplication inplace with itself" << std::endl; - - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(0); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // multiplication inplace with other - - spin_op = cudaq::spin_op(); - prod_op = cudaq::spin_operator::identity(); - - nr_reps = 1000; - std::cout << "multiplication inplace with other" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // multiplication inplace with other (reverse order) - - spin_op = cudaq::spin_op(); - prod_op = cudaq::spin_operator::identity(); - - nr_reps = 1000; - std::cout << "multiplication inplace with other (reverse order)" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // addition inplace with itself - - spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); - - nr_reps = 1000; - std::cout << "addition inplace with itself" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(0); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(0); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // addition inplace with other - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::empty(); - - nr_reps = 1000; - std::cout << "addition inplace with other" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // addition inplace with other (reverse order) - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::empty(); - - nr_reps = 1000; - std::cout << "addition inplace with other (reverse order)" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - - // addition inplace with product self - - auto spin_prod = cudaq::spin_op(); - for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(i); - spin_op = spin_prod; - prod_op = cudaq::spin_operator::identity(); - for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(i); - op_sum = prod_op; - - nr_reps = 1000; - std::cout << "addition inplace with product self" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // addition inplace with product self (reverse order) - - spin_prod = cudaq::spin_op(); - for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(100 - i); - spin_op = spin_prod; - prod_op = cudaq::spin_operator::identity(); - for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(100 - i); - op_sum = prod_op; - - nr_reps = 1000; - std::cout << "addition inplace with product self" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // product inplace with 2-term sum on fixed degrees - - auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); - spin_op = spin_term; - auto prod_term = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(1); - op_sum = prod_term; - - nr_reps = 20; - std::cout << "product inplace with 2-term sum on fixed degrees" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= spin_term; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= prod_term; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // product inplace with 2-term sum on varying degrees - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::identity(); - - nr_reps = 20; - std::cout << "product inplace with 2-term sum on varying degrees" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - // product inplace with 2-term sum on varying degrees (reverse order) - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::identity(); - - nr_reps = 20; - std::cout << "product inplace with 2-term sum on varying degrees (reverse order)" << std::endl; - - /* - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - */ - std::cout << "Old setup segfaults" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(nr_reps - i) + cudaq::spin_operator::z(nr_reps - i - 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - - // sum of products of random terms - - auto nr_degrees = 100; - std::vector spin_ops; - for (auto i = 0; i < nr_degrees; ++i) { - spin_ops.push_back(cudaq::spin::x(i)); - spin_ops.push_back(cudaq::spin::y(i)); - spin_ops.push_back(cudaq::spin::z(i)); - spin_ops.push_back(cudaq::spin::i(i)); - } - std::vector> leaf_ops; - for (auto i = 0; i < nr_degrees; ++i) { - leaf_ops.push_back(cudaq::spin_operator::x(i)); - leaf_ops.push_back(cudaq::spin_operator::y(i)); - leaf_ops.push_back(cudaq::spin_operator::z(i)); - leaf_ops.push_back(cudaq::spin_operator::i(i)); - } - - auto term_length = 1000; - auto nr_terms = 200; - srand(5); // random number seed - std::vector> indices; - for (auto i = 0; i < nr_terms; ++i) { - indices.push_back({}); - for (auto j = 0; j < term_length; ++j) - indices[i].push_back(rand() % 400); - } - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::empty(); - - std::cout << "sum of products of random terms" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_terms; ++i) { - auto term = cudaq::spin_op(); - for (auto j = 0; j < term_length; ++j) - term *= spin_ops[indices[i][j]]; - spin_op += term; - } - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_terms; ++i) { - auto term = cudaq::spin_operator::identity(); - for (auto j = 0; j < term_length; ++j) - term *= leaf_ops[indices[i][j]]; - op_sum += term; - } - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - - return 0; + // multiplication inplace with itself + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::product_operator prod_op = + cudaq::spin_operator::identity(); + + int nr_reps = 1000; + std::cout << "multiplication inplace with itself" << std::endl; + + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(0); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // multiplication inplace with other + + spin_op = cudaq::spin_op(); + prod_op = cudaq::spin_operator::identity(); + + nr_reps = 1000; + std::cout << "multiplication inplace with other" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // multiplication inplace with other (reverse order) + + spin_op = cudaq::spin_op(); + prod_op = cudaq::spin_operator::identity(); + + nr_reps = 1000; + std::cout << "multiplication inplace with other (reverse order)" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with itself + + spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); + + nr_reps = 1000; + std::cout << "addition inplace with itself" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(0); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(0); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with other + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::empty(); + + nr_reps = 1000; + std::cout << "addition inplace with other" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with other (reverse order) + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::empty(); + + nr_reps = 1000; + std::cout << "addition inplace with other (reverse order)" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(nr_reps - i); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with product self + + auto spin_prod = cudaq::spin_op(); + for (auto i = 0; i < 100; ++i) + spin_prod *= cudaq::spin::x(i); + spin_op = spin_prod; + prod_op = cudaq::spin_operator::identity(); + for (auto i = 0; i < 100; ++i) + prod_op *= cudaq::spin_operator::x(i); + op_sum = prod_op; + + nr_reps = 1000; + std::cout << "addition inplace with product self" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // addition inplace with product self (reverse order) + + spin_prod = cudaq::spin_op(); + for (auto i = 0; i < 100; ++i) + spin_prod *= cudaq::spin::x(100 - i); + spin_op = spin_prod; + prod_op = cudaq::spin_operator::identity(); + for (auto i = 0; i < 100; ++i) + prod_op *= cudaq::spin_operator::x(100 - i); + op_sum = prod_op; + + nr_reps = 1000; + std::cout << "addition inplace with product self" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // product inplace with 2-term sum on fixed degrees + + auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); + spin_op = spin_term; + auto prod_term = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(1); + op_sum = prod_term; + + nr_reps = 20; + std::cout << "product inplace with 2-term sum on fixed degrees" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= spin_term; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= prod_term; + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // product inplace with 2-term sum on varying degrees + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::identity(); + + nr_reps = 20; + std::cout << "product inplace with 2-term sum on varying degrees" + << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // product inplace with 2-term sum on varying degrees (reverse order) + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::identity(); + + nr_reps = 20; + std::cout + << "product inplace with 2-term sum on varying degrees (reverse order)" + << std::endl; + + /* + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - + 1)); stop = std::chrono::high_resolution_clock::now(); duration = + std::chrono::duration(stop - start); std::cout << "Old setup took " << + duration.count() << " seconds.\n"; + */ + std::cout << "Old setup segfaults" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(nr_reps - i) + + cudaq::spin_operator::z(nr_reps - i - 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + // sum of products of random terms + + auto nr_degrees = 100; + std::vector spin_ops; + for (auto i = 0; i < nr_degrees; ++i) { + spin_ops.push_back(cudaq::spin::x(i)); + spin_ops.push_back(cudaq::spin::y(i)); + spin_ops.push_back(cudaq::spin::z(i)); + spin_ops.push_back(cudaq::spin::i(i)); + } + std::vector> leaf_ops; + for (auto i = 0; i < nr_degrees; ++i) { + leaf_ops.push_back(cudaq::spin_operator::x(i)); + leaf_ops.push_back(cudaq::spin_operator::y(i)); + leaf_ops.push_back(cudaq::spin_operator::z(i)); + leaf_ops.push_back(cudaq::spin_operator::i(i)); + } + + auto term_length = 1000; + auto nr_terms = 200; + srand(5); // random number seed + std::vector> indices; + for (auto i = 0; i < nr_terms; ++i) { + indices.push_back({}); + for (auto j = 0; j < term_length; ++j) + indices[i].push_back(rand() % 400); + } + + spin_op = cudaq::spin_op(); + op_sum = cudaq::spin_operator::empty(); + + std::cout << "sum of products of random terms" << std::endl; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_terms; ++i) { + auto term = cudaq::spin_op(); + for (auto j = 0; j < term_length; ++j) + term *= spin_ops[indices[i][j]]; + spin_op += term; + } + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_terms; ++i) { + auto term = cudaq::spin_operator::identity(); + for (auto j = 0; j < term_length; ++j) + term *= leaf_ops[indices[i][j]]; + op_sum += term; + } + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + + return 0; } \ No newline at end of file diff --git a/runtime/cudaq/base_integrator.h b/runtime/cudaq/base_integrator.h index 9838e1a800..91d6311718 100644 --- a/runtime/cudaq/base_integrator.h +++ b/runtime/cudaq/base_integrator.h @@ -6,30 +6,29 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ - #pragma once +#pragma once - #include "operators.h" - #include "schedule.h" - #include - #include - #include - - namespace cudaq { - class BaseIntegrator { - public: - /// @brief Default constructor - BaseIntegrator() = default; - - virtual ~BaseIntegrator() = default; - - /// @brief Set the initial state and time - virtual void set_state(cudaq::state initial_state, double t0) = 0; - - /// @brief Perform integration to the target time. - virtual void integrate(double target_time) = 0; - - /// @brief Get the current time and state. - virtual std::pair get_state() = 0; - }; - } // namespace cudaq - \ No newline at end of file +#include "operators.h" +#include "schedule.h" +#include +#include +#include + +namespace cudaq { +class BaseIntegrator { +public: + /// @brief Default constructor + BaseIntegrator() = default; + + virtual ~BaseIntegrator() = default; + + /// @brief Set the initial state and time + virtual void set_state(cudaq::state initial_state, double t0) = 0; + + /// @brief Perform integration to the target time. + virtual void integrate(double target_time) = 0; + + /// @brief Get the current time and state. + virtual std::pair get_state() = 0; +}; +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/CMakeLists.txt b/runtime/cudaq/dynamics/CMakeLists.txt index 15cea1ab27..e741bd804a 100644 --- a/runtime/cudaq/dynamics/CMakeLists.txt +++ b/runtime/cudaq/dynamics/CMakeLists.txt @@ -7,7 +7,7 @@ # ============================================================================ # set(LIBRARY_NAME cudaq-operator) -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-ctad-maybe-unsupported") set(INTERFACE_POSITION_INDEPENDENT_CODE ON) set(CUDAQ_OPS_SRC @@ -21,7 +21,6 @@ set(CUDAQ_OPS_SRC schedule.cpp manipulation.cpp helpers.cpp - rydberg_hamiltonian.cpp ) add_library(${LIBRARY_NAME} SHARED ${CUDAQ_OPS_SRC}) diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 27dd0af43e..6a3560ac47 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -10,49 +10,52 @@ #include #include -#include "cudaq/utils/tensor.h" -#include "cudaq/operators.h" #include "boson_operators.h" +#include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" namespace cudaq { // private helpers std::string boson_operator::op_code_to_string() const { - if (this->op_code == 1) return "Ad"; - else if (this->op_code == 2) return "A"; - else if (this->op_code == 3) return "AdA"; - else return "I"; + if (this->op_code == 1) + return "Ad"; + else if (this->op_code == 2) + return "A"; + else if (this->op_code == 3) + return "AdA"; + else + return "I"; } // read-only properties -const std::string& boson_operator::unique_id() const { - return this->id; -} +const std::string &boson_operator::unique_id() const { return this->id; } -std::vector boson_operator::degrees() const { - return {this->target}; -} +std::vector boson_operator::degrees() const { return {this->target}; } // constructors -boson_operator::boson_operator(int target) - : op_code(0), target(target), id("I" + std::to_string(target)) {} +boson_operator::boson_operator(int target) + : op_code(0), target(target), id("I" + std::to_string(target)) {} -boson_operator::boson_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); - this->id = this->op_code_to_string() + std::to_string(target); +boson_operator::boson_operator(int target, int op_id) + : op_code(op_id), target(target) { + assert(0 <= op_id < 4); + this->id = this->op_code_to_string() + std::to_string(target); } // evaluations -matrix_2 boson_operator::to_matrix(std::unordered_map &dimensions, - const std::unordered_map> ¶meters) const { +matrix_2 boson_operator::to_matrix( + std::unordered_map &dimensions, + const std::unordered_map> ¶meters) + const { auto it = dimensions.find(this->target); if (it == dimensions.end()) - throw std::runtime_error("missing dimension for degree " + std::to_string(this->target)); + throw std::runtime_error("missing dimension for degree " + + std::to_string(this->target)); auto dim = it->second; auto mat = matrix_2(dim, dim); @@ -61,7 +64,7 @@ matrix_2 boson_operator::to_matrix(std::unordered_map &dimensions, mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } else if (this->op_code == 2) { // annihilate for (std::size_t i = 0; i + 1 < dim; i++) - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; } else if (this->op_code == 3) { // number for (std::size_t i = 0; i < dim; i++) mat[{i, i}] = static_cast(i) + 0.0j; @@ -73,8 +76,10 @@ matrix_2 boson_operator::to_matrix(std::unordered_map &dimensions, } std::string boson_operator::to_string(bool include_degrees) const { - if (include_degrees) return this->op_code_to_string() + "(" + std::to_string(target) + ")"; - else return this->op_code_to_string(); + if (include_degrees) + return this->op_code_to_string() + "(" + std::to_string(target) + ")"; + else + return this->op_code_to_string(); } // comparisons diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index b6e53096fa..ece9113129 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -12,19 +12,18 @@ #include #include -#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" namespace cudaq { -template +template class product_operator; // FIXME: rename? -class boson_operator : public operator_handler{ +class boson_operator : public operator_handler { private: - // 0 = I, 1 = Ad (create), 2 = A (annihilate), 3 = AdA (number) int op_code; int target; @@ -35,10 +34,9 @@ class boson_operator : public operator_handler{ std::string op_code_to_string() const; public: - // read-only properties - virtual const std::string& unique_id() const; + virtual const std::string &unique_id() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. @@ -57,8 +55,10 @@ class boson_operator : public operator_handler{ /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::unordered_map &dimensions, - const std::unordered_map> ¶meters = {}) const; + virtual matrix_2 + to_matrix(std::unordered_map &dimensions, + const std::unordered_map> + ¶meters = {}) const; virtual std::string to_string(bool include_degrees) const; diff --git a/runtime/cudaq/dynamics/callback.cpp b/runtime/cudaq/dynamics/callback.cpp index 6d4e461fed..4427b30bfa 100644 --- a/runtime/cudaq/dynamics/callback.cpp +++ b/runtime/cudaq/dynamics/callback.cpp @@ -10,15 +10,16 @@ #include #include -#include #include +#include #include namespace cudaq { // ScalarCallbackFunction -ScalarCallbackFunction::ScalarCallbackFunction(const ScalarCallbackFunction &other) { +ScalarCallbackFunction::ScalarCallbackFunction( + const ScalarCallbackFunction &other) { _callback_func = other._callback_func; } @@ -26,28 +27,32 @@ ScalarCallbackFunction::ScalarCallbackFunction(ScalarCallbackFunction &&other) { _callback_func = std::move(other._callback_func); } -ScalarCallbackFunction& ScalarCallbackFunction::operator=(const ScalarCallbackFunction &other) { +ScalarCallbackFunction & +ScalarCallbackFunction::operator=(const ScalarCallbackFunction &other) { if (this != &other) { _callback_func = other._callback_func; } return *this; } -ScalarCallbackFunction& ScalarCallbackFunction::operator=(ScalarCallbackFunction &&other) { +ScalarCallbackFunction & +ScalarCallbackFunction::operator=(ScalarCallbackFunction &&other) { if (this != &other) { _callback_func = std::move(other._callback_func); } return *this; } -std::complex -ScalarCallbackFunction::operator()(const std::unordered_map> ¶meters) const { +std::complex ScalarCallbackFunction::operator()( + const std::unordered_map> ¶meters) + const { return _callback_func(parameters); } // MatrixCallbackFunction -MatrixCallbackFunction::MatrixCallbackFunction(const MatrixCallbackFunction &other) { +MatrixCallbackFunction::MatrixCallbackFunction( + const MatrixCallbackFunction &other) { _callback_func = other._callback_func; } @@ -55,37 +60,45 @@ MatrixCallbackFunction::MatrixCallbackFunction(MatrixCallbackFunction &&other) { _callback_func = std::move(other._callback_func); } -MatrixCallbackFunction& MatrixCallbackFunction::operator=(const MatrixCallbackFunction &other) { +MatrixCallbackFunction & +MatrixCallbackFunction::operator=(const MatrixCallbackFunction &other) { if (this != &other) { _callback_func = other._callback_func; } return *this; } -MatrixCallbackFunction& MatrixCallbackFunction::operator=(MatrixCallbackFunction &&other) { +MatrixCallbackFunction & +MatrixCallbackFunction::operator=(MatrixCallbackFunction &&other) { if (this != &other) { _callback_func = std::move(other._callback_func); } return *this; } -matrix_2 -MatrixCallbackFunction::operator()(const std::vector &relevant_dimensions, - const std::unordered_map> ¶meters) const { +matrix_2 MatrixCallbackFunction::operator()( + const std::vector &relevant_dimensions, + const std::unordered_map> ¶meters) + const { return _callback_func(relevant_dimensions, parameters); } // Definition -Definition::Definition(std::string operator_id, const std::vector &expected_dimensions, MatrixCallbackFunction &&create) - : id(operator_id), generator(std::move(create)), m_expected_dimensions(expected_dimensions) {} +Definition::Definition(std::string operator_id, + const std::vector &expected_dimensions, + MatrixCallbackFunction &&create) + : id(operator_id), generator(std::move(create)), + m_expected_dimensions(expected_dimensions) {} -Definition::Definition(Definition &&def) - : id(def.id), generator(std::move(def.generator)), m_expected_dimensions(std::move(def.m_expected_dimensions)) {} +Definition::Definition(Definition &&def) + : id(def.id), generator(std::move(def.generator)), + m_expected_dimensions(std::move(def.m_expected_dimensions)) {} matrix_2 Definition::generate_matrix( const std::vector &relevant_dimensions, - const std::unordered_map> ¶meters) const { + const std::unordered_map> ¶meters) + const { return generator(relevant_dimensions, parameters); } diff --git a/runtime/cudaq/dynamics/callback.h b/runtime/cudaq/dynamics/callback.h index 903276b9ff..07c1a8fcad 100644 --- a/runtime/cudaq/dynamics/callback.h +++ b/runtime/cudaq/dynamics/callback.h @@ -14,24 +14,27 @@ #include #include #include -#include #include +#include #include namespace cudaq { class ScalarCallbackFunction { private: - // The user provided callback function that takes a map of complex + // The user provided callback function that takes a map of complex // parameters. - std::function(const std::unordered_map>&)> _callback_func; + std::function( + const std::unordered_map> &)> + _callback_func; public: template ScalarCallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v, Callable, - const std::unordered_map>&>, + std::is_invocable_r_v< + std::complex, Callable, + const std::unordered_map> &>, "Invalid callback function. Must have signature std::complex(" "const std::unordered_map>&)"); _callback_func = std::forward(callable); @@ -44,31 +47,36 @@ class ScalarCallbackFunction { ScalarCallbackFunction(ScalarCallbackFunction &&other); // assignment operator - ScalarCallbackFunction& operator=(const ScalarCallbackFunction &other); + ScalarCallbackFunction &operator=(const ScalarCallbackFunction &other); // move assignment operator - ScalarCallbackFunction& operator=(ScalarCallbackFunction &&other); + ScalarCallbackFunction &operator=(ScalarCallbackFunction &&other); - std::complex - operator()(const std::unordered_map> ¶meters) const; + std::complex operator()( + const std::unordered_map> ¶meters) + const; }; - class MatrixCallbackFunction { private: - // The user provided callback function that takes a vector defining the - // dimension for each degree of freedom it acts on, and a map of complex + // The user provided callback function that takes a vector defining the + // dimension for each degree of freedom it acts on, and a map of complex // parameters. - std::function&, const std::unordered_map>&)> _callback_func; + std::function &, + const std::unordered_map> &)> + _callback_func; public: template MatrixCallbackFunction(Callable &&callable) { static_assert( - std::is_invocable_r_v&, - const std::unordered_map>&>, + std::is_invocable_r_v< + matrix_2, Callable, const std::vector &, + const std::unordered_map> &>, "Invalid callback function. Must have signature " - "matrix_2(const std::vector&, const std::unordered_map>&)"); + "matrix_2(const std::vector&, const " + "std::unordered_map>&)"); _callback_func = std::forward(callable); } @@ -79,17 +87,17 @@ class MatrixCallbackFunction { MatrixCallbackFunction(MatrixCallbackFunction &&other); // assignment operator - MatrixCallbackFunction& operator=(const MatrixCallbackFunction &other); + MatrixCallbackFunction &operator=(const MatrixCallbackFunction &other); // move assignment operator - MatrixCallbackFunction& operator=(MatrixCallbackFunction &&other); + MatrixCallbackFunction &operator=(MatrixCallbackFunction &&other); matrix_2 operator()(const std::vector &relevant_dimensions, - const std::unordered_map> ¶meters) const; + const std::unordered_map> + ¶meters) const; }; - /// @brief Object used to store the definition of a custom matrix operator. class Definition { private: @@ -98,15 +106,18 @@ class Definition { std::vector m_expected_dimensions; public: - const std::vector& expected_dimensions = this->m_expected_dimensions; + const std::vector &expected_dimensions = this->m_expected_dimensions; - Definition(std::string operator_id, const std::vector &expected_dimensions, MatrixCallbackFunction &&create); + Definition(std::string operator_id, + const std::vector &expected_dimensions, + MatrixCallbackFunction &&create); Definition(Definition &&def); ~Definition(); // To call the generator function - matrix_2 generate_matrix( - const std::vector &relevant_dimensions, - const std::unordered_map> ¶meters) const; + matrix_2 + generate_matrix(const std::vector &relevant_dimensions, + const std::unordered_map> + ¶meters) const; }; } // namespace cudaq diff --git a/runtime/cudaq/dynamics/helpers.cpp b/runtime/cudaq/dynamics/helpers.cpp index de927daaa5..e9a82881a0 100644 --- a/runtime/cudaq/dynamics/helpers.cpp +++ b/runtime/cudaq/dynamics/helpers.cpp @@ -6,57 +6,59 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include #include "helpers.h" +#include namespace cudaq { namespace detail { - std::vector - generate_all_states(const std::vector °rees, const std::unordered_map &dimensions) { - if (degrees.size() == 0) - return {}; +std::vector +generate_all_states(const std::vector °rees, + const std::unordered_map &dimensions) { + if (degrees.size() == 0) + return {}; - std::vector states; - auto entry = dimensions.find(degrees[0]); - assert (entry != dimensions.end()); - for (auto state = 0; state < entry->second; state++) { - states.push_back(std::to_string(state)); - } + std::vector states; + auto entry = dimensions.find(degrees[0]); + assert(entry != dimensions.end()); + for (auto state = 0; state < entry->second; state++) { + states.push_back(std::to_string(state)); + } - for (auto idx = 1; idx < degrees.size(); ++idx) { - auto entry = dimensions.find(degrees[idx]); - assert (entry != dimensions.end()); - std::vector result; - for (auto current : states) { - for (auto state = 0; state < entry->second; state++) { - result.push_back(current + std::to_string(state)); - } + for (auto idx = 1; idx < degrees.size(); ++idx) { + auto entry = dimensions.find(degrees[idx]); + assert(entry != dimensions.end()); + std::vector result; + for (auto current : states) { + for (auto state = 0; state < entry->second; state++) { + result.push_back(current + std::to_string(state)); } - states = result; } - - return states; + states = result; } - void permute_matrix(cudaq::matrix_2 &matrix, const std::vector &permutation) { - std::vector> sorted_values; - for (std::size_t permuted : permutation) { - for (std::size_t permuted_again : permutation) { - sorted_values.push_back(matrix[{permuted, permuted_again}]); - } + return states; +} + +void permute_matrix(cudaq::matrix_2 &matrix, + const std::vector &permutation) { + std::vector> sorted_values; + for (std::size_t permuted : permutation) { + for (std::size_t permuted_again : permutation) { + sorted_values.push_back(matrix[{permuted, permuted_again}]); } - int idx = 0; - for (std::size_t row = 0; row < matrix.get_rows(); row++) { - for (std::size_t col = 0; col < matrix.get_columns(); col++) { - matrix[{row, col}] = sorted_values[idx]; - idx++; - } + } + int idx = 0; + for (std::size_t row = 0; row < matrix.get_rows(); row++) { + for (std::size_t col = 0; col < matrix.get_columns(); col++) { + matrix[{row, col}] = sorted_values[idx]; + idx++; } } +} - void canonicalize_degrees(std::vector °rees) { - std::sort(degrees.begin(), degrees.end(), std::greater()); - } +void canonicalize_degrees(std::vector °rees) { + std::sort(degrees.begin(), degrees.end(), std::greater()); +} } // namespace detail -} // namespace cudaq +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/helpers.h b/runtime/cudaq/dynamics/helpers.h index 7c0836e4dc..c123c0695e 100644 --- a/runtime/cudaq/dynamics/helpers.h +++ b/runtime/cudaq/dynamics/helpers.h @@ -6,26 +6,28 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/utils/tensor.h" #include #include -#include "cudaq/utils/tensor.h" namespace cudaq { namespace detail { - /// Generates all possible states for the given dimensions ordered according - /// to the sequence of degrees (ordering is relevant if dimensions differ). - std::vector generate_all_states(const std::vector °rees, const std::unordered_map &dimensions); - - // Permutes the given matrix according to the given permutation. - // If states is the current order of vector entries on which the given matrix - // acts, and permuted_states is the desired order of an array on which the - // permuted matrix should act, then the permutation is defined such that - // [states[i] for i in permutation] produces permuted_states. - void permute_matrix(cudaq::matrix_2 &matrix, const std::vector &permutation); +/// Generates all possible states for the given dimensions ordered according +/// to the sequence of degrees (ordering is relevant if dimensions differ). +std::vector +generate_all_states(const std::vector °rees, + const std::unordered_map &dimensions); - // Returns the degrees sorted in canonical order. - void canonicalize_degrees(std::vector °rees); -} -} +// Permutes the given matrix according to the given permutation. +// If states is the current order of vector entries on which the given matrix +// acts, and permuted_states is the desired order of an array on which the +// permuted matrix should act, then the permutation is defined such that +// [states[i] for i in permutation] produces permuted_states. +void permute_matrix(cudaq::matrix_2 &matrix, + const std::vector &permutation); +// Returns the degrees sorted in canonical order. +void canonicalize_degrees(std::vector °rees); +} // namespace detail +} // namespace cudaq diff --git a/runtime/cudaq/dynamics/manipulation.cpp b/runtime/cudaq/dynamics/manipulation.cpp index 8346526a67..fa9387cff0 100644 --- a/runtime/cudaq/dynamics/manipulation.cpp +++ b/runtime/cudaq/dynamics/manipulation.cpp @@ -16,28 +16,27 @@ namespace cudaq { // EvaluatedMatrix class -const std::vector& EvaluatedMatrix::degrees() const { +const std::vector &EvaluatedMatrix::degrees() const { return this->targets; } -const matrix_2& EvaluatedMatrix::matrix() const { - return this->value; -} +const matrix_2 &EvaluatedMatrix::matrix() const { return this->value; } -EvaluatedMatrix::EvaluatedMatrix(const std::vector °rees, const matrix_2 &matrix) - : targets(degrees), value(matrix) { +EvaluatedMatrix::EvaluatedMatrix(const std::vector °rees, + const matrix_2 &matrix) + : targets(degrees), value(matrix) { #if !defined(NDEBUG) - std::set unique_degrees; - for (auto d : degrees) - unique_degrees.insert(d); - assert(unique_degrees.size() == degrees.size()); + std::set unique_degrees; + for (auto d : degrees) + unique_degrees.insert(d); + assert(unique_degrees.size() == degrees.size()); #endif - } +} EvaluatedMatrix::EvaluatedMatrix(EvaluatedMatrix &&other) - : targets(std::move(other.targets)), value(std::move(other.value)) {} + : targets(std::move(other.targets)), value(std::move(other.value)) {} -EvaluatedMatrix& EvaluatedMatrix::operator=(EvaluatedMatrix &&other) { +EvaluatedMatrix &EvaluatedMatrix::operator=(EvaluatedMatrix &&other) { if (this != &other) { this->targets = std::move(other.targets); this->value = std::move(other.value); @@ -48,13 +47,13 @@ EvaluatedMatrix& EvaluatedMatrix::operator=(EvaluatedMatrix &&other) { // MatrixArithmetics MatrixArithmetics::MatrixArithmetics( - std::unordered_map &dimensions, - const std::unordered_map> ¶meters) - : m_dimensions(dimensions), m_parameters(parameters) {} + std::unordered_map &dimensions, + const std::unordered_map> ¶meters) + : m_dimensions(dimensions), m_parameters(parameters) {} std::vector MatrixArithmetics::compute_permutation(const std::vector &op_degrees, - const std::vector &canon_degrees) { + const std::vector &canon_degrees) { assert(op_degrees.size() == canon_degrees.size()); auto states = cudaq::detail::generate_all_states(canon_degrees, m_dimensions); @@ -85,12 +84,13 @@ MatrixArithmetics::compute_permutation(const std::vector &op_degrees, // Returns: // A tuple consisting of the permuted matrix as well as the sequence of // degrees of freedom in canonical order. -void MatrixArithmetics::canonicalize(matrix_2 &matrix, std::vector °rees) { +void MatrixArithmetics::canonicalize(matrix_2 &matrix, + std::vector °rees) { auto current_degrees = degrees; cudaq::detail::canonicalize_degrees(degrees); if (current_degrees != degrees) { auto permutation = this->compute_permutation(current_degrees, degrees); - cudaq::detail::permute_matrix(matrix, permutation); + cudaq::detail::permute_matrix(matrix, permutation); } } diff --git a/runtime/cudaq/dynamics/manipulation.h b/runtime/cudaq/dynamics/manipulation.h index f74ba54c95..65c3f4188a 100644 --- a/runtime/cudaq/dynamics/manipulation.h +++ b/runtime/cudaq/dynamics/manipulation.h @@ -6,11 +6,11 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#pragma once +#pragma once +#include "cudaq/utils/tensor.h" #include #include -#include "cudaq/utils/tensor.h" namespace cudaq { @@ -20,7 +20,7 @@ class OperatorArithmetics { /// @brief Accesses the relevant data to evaluate an operator expression /// in the leaf nodes, that is in elementary and scalar operators. // template - //TEval evaluate(HandlerTy &op); + // TEval evaluate(HandlerTy &op); /// @brief Adds two operators that act on the same degrees of freedom. TEval add(TEval val1, TEval val2); @@ -35,23 +35,22 @@ class OperatorArithmetics { class EvaluatedMatrix { private: - std::vector targets; matrix_2 value; public: - const std::vector& degrees() const; + const std::vector °rees() const; - const matrix_2& matrix() const; + const matrix_2 &matrix() const; EvaluatedMatrix(const std::vector °rees, const matrix_2 &matrix); EvaluatedMatrix(EvaluatedMatrix &&other); // delete copy constructor and copy assignment to avoid unnecessary copies EvaluatedMatrix(const EvaluatedMatrix &other) = delete; - EvaluatedMatrix& operator=(const EvaluatedMatrix &other) = delete; + EvaluatedMatrix &operator=(const EvaluatedMatrix &other) = delete; - EvaluatedMatrix& operator=(EvaluatedMatrix &&other); + EvaluatedMatrix &operator=(EvaluatedMatrix &&other); }; /// Encapsulates the functions needed to compute the matrix representation @@ -60,15 +59,16 @@ class MatrixArithmetics : public OperatorArithmetics { private: std::vector compute_permutation(const std::vector &op_degrees, const std::vector &canon_degrees); - + void canonicalize(matrix_2 &op_matrix, std::vector &op_degrees); public: std::unordered_map m_dimensions; // may be updated during evaluation const std::unordered_map> m_parameters; - MatrixArithmetics(std::unordered_map &dimensions, - const std::unordered_map> ¶meters); + MatrixArithmetics( + std::unordered_map &dimensions, + const std::unordered_map> ¶meters); // Computes the tensor product of two evaluate operators that act on // different degrees of freedom using the kronecker product. @@ -81,4 +81,4 @@ class MatrixArithmetics : public OperatorArithmetics { EvaluatedMatrix add(EvaluatedMatrix op1, EvaluatedMatrix op2); }; -} \ No newline at end of file +} // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index a32e33f825..4954a80d5e 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -10,11 +10,11 @@ #include #include -#include "cudaq/utils/tensor.h" +#include "boson_operators.h" #include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" #include "matrix_operators.h" #include "spin_operators.h" -#include "boson_operators.h" namespace cudaq { @@ -31,59 +31,72 @@ std::string matrix_operator::type_prefix() { return typeid(T).name(); } -// no need to prefix the operator id and op code with the type name for these (same names mean the same thing) -template<> std::string matrix_operator::type_prefix() { return ""; } -template<> std::string matrix_operator::type_prefix() { return ""; } +// no need to prefix the operator id and op code with the type name for these +// (same names mean the same thing) +template <> +std::string matrix_operator::type_prefix() { + return ""; +} +template <> +std::string matrix_operator::type_prefix() { + return ""; +} -void matrix_operator::define(std::string operator_id, std::vector expected_dimensions, - MatrixCallbackFunction &&create) { - auto defn = Definition(operator_id, expected_dimensions, std::forward(create)); +void matrix_operator::define(std::string operator_id, + std::vector expected_dimensions, + MatrixCallbackFunction &&create) { + auto defn = Definition(operator_id, expected_dimensions, + std::forward(create)); auto result = matrix_operator::m_ops.insert({operator_id, std::move(defn)}); if (!result.second) { - throw std::runtime_error("an matrix operator with name " + operator_id + "is already defined"); + throw std::runtime_error("an matrix operator with name " + operator_id + + "is already defined"); } } -product_operator matrix_operator::instantiate(std::string operator_id, const std::vector °rees) { +product_operator +matrix_operator::instantiate(std::string operator_id, + const std::vector °rees) { auto it = matrix_operator::m_ops.find(operator_id); - if (it == matrix_operator::m_ops.end()) - throw std::range_error("not matrix operator with the name '" + operator_id + "' has been defined"); + if (it == matrix_operator::m_ops.end()) + throw std::range_error("not matrix operator with the name '" + operator_id + + "' has been defined"); return product_operator(matrix_operator(operator_id, degrees)); } -product_operator matrix_operator::instantiate(std::string operator_id, std::vector &°rees) { +product_operator +matrix_operator::instantiate(std::string operator_id, + std::vector &°rees) { auto it = matrix_operator::m_ops.find(operator_id); - if (it == matrix_operator::m_ops.end()) - throw std::range_error("not matrix operator with the name '" + operator_id + "' has been defined"); + if (it == matrix_operator::m_ops.end()) + throw std::range_error("not matrix operator with the name '" + operator_id + + "' has been defined"); return product_operator(matrix_operator(operator_id, std::move(degrees))); } // read-only properties -const std::string& matrix_operator::unique_id() const { - return this->id; -} +const std::string &matrix_operator::unique_id() const { return this->id; } -std::vector matrix_operator::degrees() const { - return this->targets; -} +std::vector matrix_operator::degrees() const { return this->targets; } // constructors matrix_operator::matrix_operator(int degree) { std::string op_code = "identity"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - - // Build up the identity matrix. - for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = 1.0 + 0.0j; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + + // Build up the identity matrix. + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = 1.0 + 0.0j; + } + return mat; + }; matrix_operator::define(op_code, {-1}, std::move(func)); } this->op_code = op_code; @@ -93,39 +106,48 @@ matrix_operator::matrix_operator(int degree) { this->id += std::to_string(t); } -matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) - : op_code(operator_id), targets(degrees), id(operator_id) { - assert(this->targets.size() > 0); - for (auto t : this->targets) - this->id += std::to_string(t); - } +matrix_operator::matrix_operator(std::string operator_id, + const std::vector °rees) + : op_code(operator_id), targets(degrees), id(operator_id) { + assert(this->targets.size() > 0); + for (auto t : this->targets) + this->id += std::to_string(t); +} -matrix_operator::matrix_operator(std::string operator_id, std::vector &°rees) - : op_code(operator_id), targets(std::move(degrees)), id(operator_id) { - assert(this->targets.size() > 0); - for (auto t : this->targets) - this->id += std::to_string(t); - } +matrix_operator::matrix_operator(std::string operator_id, + std::vector &°rees) + : op_code(operator_id), targets(std::move(degrees)), id(operator_id) { + assert(this->targets.size() > 0); + for (auto t : this->targets) + this->id += std::to_string(t); +} -template, bool>> +template , bool>> matrix_operator::matrix_operator(const T &other) { std::string type_prefix = matrix_operator::type_prefix(); this->targets = other.degrees(); - this->op_code = type_prefix + other.to_string(false) + std::to_string(this->targets.size()); + this->op_code = type_prefix + other.to_string(false) + + std::to_string(this->targets.size()); this->id = type_prefix + other.unique_id(); for (auto t : this->targets) this->id += std::to_string(t); - if (matrix_operator::m_ops.find(this->op_code) == matrix_operator::m_ops.end()) { - auto func = [targets = other.degrees(), other] - (const std::vector &dimensions, const std::unordered_map> &_none) { + if (matrix_operator::m_ops.find(this->op_code) == + matrix_operator::m_ops.end()) { + auto func = [targets = other.degrees(), other]( + const std::vector &dimensions, + const std::unordered_map> + &_none) { std::unordered_map dims; - for(auto i = 0; i < dimensions.size(); ++i) + for (auto i = 0; i < dimensions.size(); ++i) dims[targets[i]] = dimensions[i]; return other.to_matrix(dims, std::move(_none)); }; - // the to_matrix method on the spin op will check the dimensions, so we allow arbitrary here - std::vector required_dimensions (this->targets.size(), -1); - matrix_operator::define(this->op_code, std::move(required_dimensions), func); + // the to_matrix method on the spin op will check the dimensions, so we + // allow arbitrary here + std::vector required_dimensions(this->targets.size(), -1); + matrix_operator::define(this->op_code, std::move(required_dimensions), + func); } } @@ -133,14 +155,14 @@ template matrix_operator::matrix_operator(const spin_operator &other); template matrix_operator::matrix_operator(const boson_operator &other); matrix_operator::matrix_operator(const matrix_operator &other) - : targets(other.targets), op_code(other.op_code), id(other.id) {} + : targets(other.targets), op_code(other.op_code), id(other.id) {} -matrix_operator::matrix_operator(matrix_operator &&other) - : targets(std::move(other.targets)), op_code(other.op_code), id(other.id) {} +matrix_operator::matrix_operator(matrix_operator &&other) + : targets(std::move(other.targets)), op_code(other.op_code), id(other.id) {} // assignments -matrix_operator& matrix_operator::operator=(const matrix_operator& other) { +matrix_operator &matrix_operator::operator=(const matrix_operator &other) { if (this != &other) { this->targets = other.targets; this->op_code = other.op_code; @@ -149,16 +171,21 @@ matrix_operator& matrix_operator::operator=(const matrix_operator& other) { return *this; } -template::value && std::is_base_of_v, bool>> -matrix_operator& matrix_operator::operator=(const T& other) { +template ::value && + std::is_base_of_v, + bool>> +matrix_operator &matrix_operator::operator=(const T &other) { *this = matrix_operator(other); return *this; } -template matrix_operator& matrix_operator::operator=(const spin_operator& other); -template matrix_operator& matrix_operator::operator=(const boson_operator& other); +template matrix_operator & +matrix_operator::operator=(const spin_operator &other); +template matrix_operator & +matrix_operator::operator=(const boson_operator &other); -matrix_operator& matrix_operator::operator=(matrix_operator &&other) { +matrix_operator &matrix_operator::operator=(matrix_operator &&other) { if (this != &other) { this->targets = std::move(other.targets); this->op_code = other.op_code; @@ -171,9 +198,10 @@ matrix_operator& matrix_operator::operator=(matrix_operator &&other) { matrix_2 matrix_operator::to_matrix( std::unordered_map &dimensions, - const std::unordered_map> ¶meters) const { + const std::unordered_map> ¶meters) + const { auto it = matrix_operator::m_ops.find(this->op_code); - if (it == matrix_operator::m_ops.end()) + if (it == matrix_operator::m_ops.end()) throw std::range_error("unable to find operator"); std::vector relevant_dimensions; @@ -183,15 +211,16 @@ matrix_2 matrix_operator::to_matrix( auto expected_dim = it->second.expected_dimensions[i]; if (expected_dim <= 0) { if (entry == dimensions.end()) - throw std::runtime_error("missing dimension for degree " + std::to_string(this->targets[i])); + throw std::runtime_error("missing dimension for degree " + + std::to_string(this->targets[i])); relevant_dimensions.push_back(entry->second); } else { if (entry == dimensions.end()) dimensions[this->targets[i]] = expected_dim; else if (entry->second != expected_dim) - throw std::runtime_error("invalid dimension for degree " + - std::to_string(this->targets[i]) + - ", expected dimension is " + std::to_string(expected_dim)); + throw std::runtime_error( + "invalid dimension for degree " + std::to_string(this->targets[i]) + + ", expected dimension is " + std::to_string(expected_dim)); relevant_dimensions.push_back(expected_dim); } } @@ -200,8 +229,10 @@ matrix_2 matrix_operator::to_matrix( } std::string matrix_operator::to_string(bool include_degrees) const { - if (!include_degrees) return this->op_code; - else if (this->targets.size() == 0) return this->op_code + "()"; + if (!include_degrees) + return this->op_code; + else if (this->targets.size() == 0) + return this->op_code + "()"; auto it = this->targets.cbegin(); std::string str = this->op_code + "(" + std::to_string(*it++); while (it != this->targets.cend()) @@ -232,15 +263,16 @@ product_operator matrix_operator::identity(int degree) { product_operator matrix_operator::annihilate(int degree) { std::string op_code = "annihilate"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i + 1 < dimension; i++) { - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -250,15 +282,16 @@ product_operator matrix_operator::annihilate(int degree) { product_operator matrix_operator::create(int degree) { std::string op_code = "create"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i + 1 < dimension; i++) { - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -268,19 +301,20 @@ product_operator matrix_operator::create(int degree) { product_operator matrix_operator::position(int degree) { std::string op_code = "position"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - // position = 0.5 * (create + annihilate) - for (std::size_t i = 0; i + 1 < dimension; i++) { - mat[{i + 1, i}] = - 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = - 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + // position = 0.5 * (create + annihilate) + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = + 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + 0.5 * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -290,19 +324,21 @@ product_operator matrix_operator::position(int degree) { product_operator matrix_operator::momentum(int degree) { std::string op_code = "momentum"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - // momentum = 0.5j * (create - annihilate) - for (std::size_t i = 0; i + 1 < dimension; i++) { - mat[{i + 1, i}] = - (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - mat[{i, i + 1}] = - -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + // momentum = 0.5j * (create - annihilate) + for (std::size_t i = 0; i + 1 < dimension; i++) { + mat[{i + 1, i}] = + (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + mat[{i, i + 1}] = + -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + + 0.0 * 'j'; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -312,15 +348,16 @@ product_operator matrix_operator::momentum(int degree) { product_operator matrix_operator::number(int degree) { std::string op_code = "number"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = static_cast(i) + 0.0j; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = static_cast(i) + 0.0j; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -330,15 +367,16 @@ product_operator matrix_operator::number(int degree) { product_operator matrix_operator::parity(int degree) { std::string op_code = "parity"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; - } - return mat; - }; + auto func = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -349,11 +387,12 @@ product_operator matrix_operator::displace(int degree) { std::string op_code = "displace"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, - const std::unordered_map> ¶meters) { + const std::unordered_map> + ¶meters) { std::size_t dimension = dimensions[0]; auto entry = parameters.find("displacement"); if (entry == parameters.end()) - throw std::runtime_error("missing value for parameter 'displacement'"); + throw std::runtime_error("missing value for parameter 'displacement'"); auto displacement_amplitude = entry->second; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); @@ -376,11 +415,12 @@ product_operator matrix_operator::squeeze(int degree) { std::string op_code = "squeeze"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, - const std::unordered_map> ¶meters) { + const std::unordered_map> + ¶meters) { std::size_t dimension = dimensions[0]; auto entry = parameters.find("squeezing"); if (entry == parameters.end()) - throw std::runtime_error("missing value for parameter 'squeezing'"); + throw std::runtime_error("missing value for parameter 'squeezing'"); auto squeezing = entry->second; auto create = matrix_2(dimension, dimension); auto annihilate = matrix_2(dimension, dimension); diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index ec46beaceb..cf3ef41581 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -8,22 +8,21 @@ #pragma once +#include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" +#include "templates.h" #include #include #include -#include "cudaq/utils/tensor.h" -#include "cudaq/operators.h" - namespace cudaq { -template +template class product_operator; -class matrix_operator : public operator_handler{ +class matrix_operator : public operator_handler { private: - static std::unordered_map m_ops; // used when converting other operators to matrix operators @@ -31,7 +30,6 @@ class matrix_operator : public operator_handler{ static std::string type_prefix(); protected: - std::vector targets; std::string op_code; std::string id; @@ -41,7 +39,9 @@ class matrix_operator : public operator_handler{ public: #if !defined(NDEBUG) - static bool can_be_canonicalized; // needs to be false; no canonical order can be defined for matrix operator expressions + static bool + can_be_canonicalized; // needs to be false; no canonical order can be + // defined for matrix operator expressions #endif // tools for custom operators @@ -72,22 +72,27 @@ class matrix_operator : public operator_handler{ /// degree of freedom, and an argument called `dimensions` (or `dims` for /// short), if the operator acts /// on multiple degrees of freedom. - static void define(std::string operator_id, std::vector expected_dimensions, + static void define(std::string operator_id, + std::vector expected_dimensions, MatrixCallbackFunction &&create); /// @brief Instantiates a custom operator. - /// @arg operator_id : The ID of the operator as specified when it was defined. + /// @arg operator_id : The ID of the operator as specified when it was + /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - static product_operator instantiate(std::string operator_id, const std::vector °rees); + static product_operator + instantiate(std::string operator_id, const std::vector °rees); /// @brief Instantiates a custom operator. - /// @arg operator_id : The ID of the operator as specified when it was defined. + /// @arg operator_id : The ID of the operator as specified when it was + /// defined. /// @arg degrees : the degrees of freedom that the operator acts upon. - static product_operator instantiate(std::string operator_id, std::vector &°rees); + static product_operator + instantiate(std::string operator_id, std::vector &°rees); // read-only properties - virtual const std::string& unique_id() const; + virtual const std::string &unique_id() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. @@ -97,7 +102,8 @@ class matrix_operator : public operator_handler{ matrix_operator(int target); - template, bool> = true> + template , + bool> = true> matrix_operator(const T &other); // copy constructor @@ -110,14 +116,17 @@ class matrix_operator : public operator_handler{ // assignments - template::value && std::is_base_of_v, bool> = true> - matrix_operator& operator=(const T& other); + template ::value && + std::is_base_of_v, + bool> = true> + matrix_operator &operator=(const T &other); // assignment operator - matrix_operator& operator=(const matrix_operator& other); + matrix_operator &operator=(const matrix_operator &other); // move assignment operator - matrix_operator& operator=(matrix_operator &&other); + matrix_operator &operator=(matrix_operator &&other); // evaluations @@ -126,8 +135,10 @@ class matrix_operator : public operator_handler{ /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::unordered_map &dimensions, - const std::unordered_map> ¶meters = {}) const; + virtual matrix_2 + to_matrix(std::unordered_map &dimensions, + const std::unordered_map> + ¶meters = {}) const; virtual std::string to_string(bool include_degrees) const; diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h index 10b4e5aa72..ee82e9e164 100644 --- a/runtime/cudaq/dynamics/operator_leafs.h +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -27,11 +27,12 @@ class scalar_operator { std::variant, ScalarCallbackFunction> value; public: - // constructors and destructors scalar_operator(double value); + bool is_constant() const; + /// @brief Constructor that just takes and returns a complex double value. /// @NOTE: This replicates the behavior of the python `scalar_operator::const` /// without the need for an extra member function. @@ -54,20 +55,22 @@ class scalar_operator { // assignments // assignment operator - scalar_operator& operator=(const scalar_operator &other); + scalar_operator &operator=(const scalar_operator &other); // move assignment operator - scalar_operator& operator=(scalar_operator &&other); + scalar_operator &operator=(scalar_operator &&other); // evaluations /// @brief Return the scalar operator as a concrete complex value. std::complex - evaluate(const std::unordered_map> ¶meters = {}) const; + evaluate(const std::unordered_map> + ¶meters = {}) const; // Return the scalar operator as a 1x1 matrix. This is needed for // compatibility with the other inherited classes. - matrix_2 to_matrix(const std::unordered_map> ¶meters = {}) const; + matrix_2 to_matrix(const std::unordered_map> + ¶meters = {}) const; // comparisons @@ -84,35 +87,39 @@ class scalar_operator { scalar_operator operator/(double other) const; scalar_operator operator+(double other) const; scalar_operator operator-(double other) const; - scalar_operator& operator*=(double other); - scalar_operator& operator/=(double other); - scalar_operator& operator+=(double other); - scalar_operator& operator-=(double other); + scalar_operator &operator*=(double other); + scalar_operator &operator/=(double other); + scalar_operator &operator+=(double other); + scalar_operator &operator-=(double other); scalar_operator operator*(std::complex other) const; scalar_operator operator/(std::complex other) const; scalar_operator operator+(std::complex other) const; scalar_operator operator-(std::complex other) const; - scalar_operator& operator*=(std::complex other); - scalar_operator& operator/=(std::complex other); - scalar_operator& operator+=(std::complex other); - scalar_operator& operator-=(std::complex other); + scalar_operator &operator*=(std::complex other); + scalar_operator &operator/=(std::complex other); + scalar_operator &operator+=(std::complex other); + scalar_operator &operator-=(std::complex other); scalar_operator operator*(const scalar_operator &other) const; scalar_operator operator/(const scalar_operator &other) const; scalar_operator operator+(const scalar_operator &other) const; scalar_operator operator-(const scalar_operator &other) const; - scalar_operator& operator*=(const scalar_operator &other); - scalar_operator& operator/=(const scalar_operator &other); - scalar_operator& operator+=(const scalar_operator &other); - scalar_operator& operator-=(const scalar_operator &other); + scalar_operator &operator*=(const scalar_operator &other); + scalar_operator &operator/=(const scalar_operator &other); + scalar_operator &operator+=(const scalar_operator &other); + scalar_operator &operator-=(const scalar_operator &other); friend scalar_operator operator*(scalar_operator &&self, double other); friend scalar_operator operator/(scalar_operator &&self, double other); friend scalar_operator operator+(scalar_operator &&self, double other); friend scalar_operator operator-(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, std::complex other); - friend scalar_operator operator/(scalar_operator &&self, std::complex other); - friend scalar_operator operator+(scalar_operator &&self, std::complex other); - friend scalar_operator operator-(scalar_operator &&self, std::complex other); + friend scalar_operator operator+(scalar_operator &&self, + std::complex other); + friend scalar_operator operator/(scalar_operator &&self, + std::complex other); + friend scalar_operator operator+(scalar_operator &&self, + std::complex other); + friend scalar_operator operator-(scalar_operator &&self, + std::complex other); // left-hand arithmetics @@ -120,13 +127,16 @@ class scalar_operator { friend scalar_operator operator/(double other, const scalar_operator &self); friend scalar_operator operator+(double other, const scalar_operator &self); friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, const scalar_operator &self); - friend scalar_operator operator/(std::complex other, const scalar_operator &self); - friend scalar_operator operator+(std::complex other, const scalar_operator &self); - friend scalar_operator operator-(std::complex other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, + const scalar_operator &self); + friend scalar_operator operator/(std::complex other, + const scalar_operator &self); + friend scalar_operator operator+(std::complex other, + const scalar_operator &self); + friend scalar_operator operator-(std::complex other, + const scalar_operator &self); }; - template class product_operator; @@ -136,12 +146,13 @@ class operator_sum; class operator_handler { public: #if !defined(NDEBUG) - static bool can_be_canonicalized; // whether a canonical order can be defined for operator expressions + static bool can_be_canonicalized; // whether a canonical order can be defined + // for operator expressions #endif virtual ~operator_handler() = default; - virtual const std::string& unique_id() const = 0; + virtual const std::string &unique_id() const = 0; virtual std::vector degrees() const = 0; @@ -150,15 +161,20 @@ class operator_handler { /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::unordered_map &dimensions, - const std::unordered_map> ¶meters = {}) const = 0; + virtual matrix_2 + to_matrix(std::unordered_map &dimensions, + const std::unordered_map> + ¶meters = {}) const = 0; virtual std::string to_string(bool include_degrees = true) const = 0; template static operator_sum empty(); - template...>::value, bool> = true> + template < + typename HandlerTy, typename... Args, + std::enable_if_t...>::value, + bool> = true> static product_operator identity(Args... targets); }; diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 0ae212069d..bde0178f21 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -11,67 +11,75 @@ #include #include +#include "boson_operators.h" #include "cudaq/operators.h" #include "helpers.h" #include "manipulation.h" #include "matrix_operators.h" #include "spin_operators.h" -#include "boson_operators.h" namespace cudaq { // private methods -template +template void operator_sum::insert(product_operator &&other) { - auto term_id = other.term_id; // need to copy string since both the operator and the key need it + auto term_id = other.term_id; // need to copy string since both the operator + // and the key need it auto it = this->tmap.find(term_id); - if (it == this->tmap.end()) this->tmap.insert(std::make_pair(term_id, std::move(other))); - else it->second.coefficient += other.coefficient; + if (it == this->tmap.end()) + this->tmap.insert(std::make_pair(term_id, std::move(other))); + else + it->second.coefficient += other.coefficient; } -template +template void operator_sum::insert(const product_operator &other) { auto it = this->tmap.find(other.term_id); - if (it == this->tmap.end()) this->tmap.insert(std::make_pair(other.term_id, other)); - else it->second.coefficient += other.coefficient; + if (it == this->tmap.end()) + this->tmap.insert(std::make_pair(other.term_id, other)); + else + it->second.coefficient += other.coefficient; } -template +template void operator_sum::aggregate_terms() {} -template -template -void operator_sum::aggregate_terms(product_operator &&head, Args&& ... args) { +template +template +void operator_sum::aggregate_terms( + product_operator &&head, Args &&...args) { this->insert(std::forward>(head)); aggregate_terms(std::forward(args)...); } template -EvaluatedMatrix operator_sum::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const { +EvaluatedMatrix +operator_sum::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const { auto terms = this->get_terms(); auto degrees = this->degrees(); // We need to make sure all matrices are of the same size to sum them up. - auto paddedTerm = - [&arithmetics, °rees = std::as_const(degrees)](product_operator &&term) { - std::vector prod_ops; - prod_ops.reserve(degrees.size()); - auto term_degrees = term.degrees(); - std::string term_id = ""; - for (auto degree : degrees) { - auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); - if (it == term_degrees.end()) { - HandlerTy identity(degree); - term_id += identity.unique_id(); - prod_ops.push_back(std::move(identity)); - } + auto paddedTerm = [&arithmetics, °rees = std::as_const(degrees)]( + product_operator &&term) { + std::vector prod_ops; + prod_ops.reserve(degrees.size()); + auto term_degrees = term.degrees(); + std::string term_id = ""; + for (auto degree : degrees) { + auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); + if (it == term_degrees.end()) { + HandlerTy identity(degree); + term_id += identity.unique_id(); + prod_ops.push_back(std::move(identity)); } - product_operator prod(1, std::move(prod_ops), std::move(term_id)); - prod *= term; // ensures canonical ordering - return prod; + } + product_operator prod(1, std::move(prod_ops), + std::move(term_id)); + prod *= term; // ensures canonical ordering + return prod; }; if (pad_terms) { @@ -93,22 +101,21 @@ EvaluatedMatrix operator_sum::m_evaluate( } } -#define INSTANTIATE_SUM_PRIVATE_METHODS(HandlerTy) \ - \ - template \ - void operator_sum::aggregate_terms(product_operator &&item2); \ - \ - template \ - void operator_sum::aggregate_terms(product_operator &&item1, \ - product_operator &&item2); \ - \ - template \ - void operator_sum::aggregate_terms(product_operator &&item1, \ - product_operator &&item2, \ - product_operator &&item3); \ - \ - template \ - EvaluatedMatrix operator_sum::m_evaluate( \ +#define INSTANTIATE_SUM_PRIVATE_METHODS(HandlerTy) \ + \ + template void operator_sum::aggregate_terms( \ + product_operator &&item2); \ + \ + template void operator_sum::aggregate_terms( \ + product_operator &&item1, \ + product_operator &&item2); \ + \ + template void operator_sum::aggregate_terms( \ + product_operator &&item1, \ + product_operator &&item2, \ + product_operator &&item3); \ + \ + template EvaluatedMatrix operator_sum::m_evaluate( \ MatrixArithmetics arithmetics, bool pad_terms) const; INSTANTIATE_SUM_PRIVATE_METHODS(matrix_operator); @@ -126,35 +133,35 @@ std::vector operator_sum::degrees() const { unsorted_degrees.insert(op_degrees.cbegin(), op_degrees.cend()); } } - auto degrees = std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); + auto degrees = + std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); cudaq::detail::canonicalize_degrees(degrees); return degrees; } -template -int operator_sum::num_terms() const { - return this->tmap.size(); +template +int operator_sum::num_terms() const { + return this->tmap.size(); } -template -std::vector> operator_sum::get_terms() const { +template +std::vector> +operator_sum::get_terms() const { std::vector> prods; prods.reserve(this->tmap.size()); for (const auto &entry : this->tmap) { - prods.push_back(entry.second); + prods.push_back(entry.second); } - return prods; + return prods; } -#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ - \ - template \ - std::vector operator_sum::degrees() const; \ - \ - template \ - int operator_sum::num_terms() const; \ - \ - template \ - std::vector> operator_sum::get_terms() const; +#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ + \ + template std::vector operator_sum::degrees() const; \ + \ + template int operator_sum::num_terms() const; \ + \ + template std::vector> \ + operator_sum::get_terms() const; INSTANTIATE_SUM_PROPERTIES(matrix_operator); INSTANTIATE_SUM_PROPERTIES(spin_operator); @@ -162,27 +169,34 @@ INSTANTIATE_SUM_PROPERTIES(boson_operator); // constructors -template +template operator_sum::operator_sum(const product_operator &prod) { this->insert(prod); } -template -template, Args>...>::value, bool>> -operator_sum::operator_sum(Args&&... args) { +template +template , Args>...>::value, + bool>> +operator_sum::operator_sum(Args &&...args) { this->tmap.reserve(sizeof...(Args)); - aggregate_terms(std::forward&&>(args)...); + aggregate_terms(std::forward &&>(args)...); } -template -operator_sum::operator_sum(std::vector> &&terms) { +template +operator_sum::operator_sum( + std::vector> &&terms) { this->tmap.reserve(terms.size()); for (auto &&term : terms) this->insert(std::move(term)); } -template -template::value && std::is_constructible::value, bool>> +template +template ::value && + std::is_constructible::value, + bool>> operator_sum::operator_sum(const operator_sum &other) { this->tmap.reserve(other.tmap.size()); for (const auto &entry : other.tmap) { @@ -191,44 +205,44 @@ operator_sum::operator_sum(const operator_sum &other) { } } -template +template operator_sum::operator_sum(const operator_sum &other) - : tmap(other.tmap) {} - -template -operator_sum::operator_sum(operator_sum &&other) - : tmap(std::move(other.tmap)) {} - -#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ - \ - template \ - operator_sum::operator_sum(const product_operator &item2); \ - \ - template \ - operator_sum::operator_sum(product_operator &&item2); \ - \ - template \ - operator_sum::operator_sum(product_operator &&item1, \ - product_operator &&item2); \ - \ - template \ - operator_sum::operator_sum(product_operator &&item1, \ - product_operator &&item2, \ - product_operator &&item3); \ - \ - template \ - operator_sum::operator_sum(std::vector> &&terms); \ - \ - template \ - operator_sum::operator_sum(const operator_sum &other); \ - \ - template \ - operator_sum::operator_sum(operator_sum &&other); - -template -operator_sum::operator_sum(const operator_sum &other); -template -operator_sum::operator_sum(const operator_sum &other); + : tmap(other.tmap) {} + +template +operator_sum::operator_sum(operator_sum &&other) + : tmap(std::move(other.tmap)) {} + +#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ + \ + template operator_sum::operator_sum( \ + const product_operator &item2); \ + \ + template operator_sum::operator_sum( \ + product_operator &&item2); \ + \ + template operator_sum::operator_sum( \ + product_operator &&item1, \ + product_operator &&item2); \ + \ + template operator_sum::operator_sum( \ + product_operator &&item1, \ + product_operator &&item2, \ + product_operator &&item3); \ + \ + template operator_sum::operator_sum( \ + std::vector> &&terms); \ + \ + template operator_sum::operator_sum( \ + const operator_sum &other); \ + \ + template operator_sum::operator_sum( \ + operator_sum &&other); + +template operator_sum::operator_sum( + const operator_sum &other); +template operator_sum::operator_sum( + const operator_sum &other); INSTANTIATE_SUM_CONSTRUCTORS(matrix_operator); INSTANTIATE_SUM_CONSTRUCTORS(spin_operator); @@ -236,78 +250,90 @@ INSTANTIATE_SUM_CONSTRUCTORS(boson_operator); // assignments -template - template::value && std::is_constructible::value, bool>> -operator_sum& operator_sum::operator=(const product_operator &other) { +template +template ::value && + std::is_constructible::value, + bool>> +operator_sum & +operator_sum::operator=(const product_operator &other) { this->tmap.clear(); product_operator prod(other); this->insert(std::move(prod)); return *this; } -template -operator_sum& operator_sum::operator=(product_operator &&other) { +template +operator_sum & +operator_sum::operator=(product_operator &&other) { this->tmap.clear(); this->insert(std::move(other)); return *this; } -template -operator_sum& operator_sum::operator=(const product_operator &other) { +template +operator_sum & +operator_sum::operator=(const product_operator &other) { this->tmap.clear(); this->insert(other); return *this; } -template -template::value && std::is_constructible::value, bool>> -operator_sum& operator_sum::operator=(const operator_sum &other) { +template +template ::value && + std::is_constructible::value, + bool>> +operator_sum & +operator_sum::operator=(const operator_sum &other) { *this = operator_sum(other); return *this; } -template -operator_sum& operator_sum::operator=(const operator_sum &other) { +template +operator_sum & +operator_sum::operator=(const operator_sum &other) { if (this != &other) { - this->tmap = other.tmap; + this->tmap = other.tmap; } return *this; } -template -operator_sum& operator_sum::operator=(operator_sum &&other) { +template +operator_sum & +operator_sum::operator=(operator_sum &&other) { if (this != &other) { - this->tmap = std::move(other.tmap); + this->tmap = std::move(other.tmap); } return *this; } -#define INSTANTIATE_SUM_ASSIGNMENTS(HandlerTy) \ - \ - template \ - operator_sum& operator_sum::operator=( \ - product_operator &&other); \ - \ - template \ - operator_sum& operator_sum::operator=( \ - const product_operator &other); \ - \ - template \ - operator_sum& operator_sum::operator=( \ - const operator_sum &other); \ - \ - template \ - operator_sum& operator_sum::operator=( \ - operator_sum &&other); - -template -operator_sum& operator_sum::operator=(const product_operator &other); -template -operator_sum& operator_sum::operator=(const product_operator &other); -template -operator_sum& operator_sum::operator=(const operator_sum &other); -template -operator_sum& operator_sum::operator=(const operator_sum &other); +#define INSTANTIATE_SUM_ASSIGNMENTS(HandlerTy) \ + \ + template operator_sum &operator_sum::operator=( \ + product_operator &&other); \ + \ + template operator_sum &operator_sum::operator=( \ + const product_operator &other); \ + \ + template operator_sum &operator_sum::operator=( \ + const operator_sum &other); \ + \ + template operator_sum &operator_sum::operator=( \ + operator_sum &&other); + +template operator_sum & +operator_sum::operator=( + const product_operator &other); +template operator_sum & +operator_sum::operator=( + const product_operator &other); +template operator_sum & +operator_sum::operator=( + const operator_sum &other); +template operator_sum & +operator_sum::operator=( + const operator_sum &other); INSTANTIATE_SUM_ASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_ASSIGNMENTS(spin_operator); @@ -320,21 +346,22 @@ std::string operator_sum::to_string() const { throw std::runtime_error("not implemented"); } -template -matrix_2 operator_sum::to_matrix(std::unordered_map dimensions, - const std::unordered_map> ¶meters) const { +template +matrix_2 operator_sum::to_matrix( + std::unordered_map dimensions, + const std::unordered_map> ¶meters) + const { return m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); } -#define INSTANTIATE_SUM_EVALUATIONS(HandlerTy) \ - \ - template \ - std::string operator_sum::to_string() const; \ - \ - template \ - matrix_2 operator_sum::to_matrix( \ - std::unordered_map dimensions, \ - const std::unordered_map> ¶ms) const; +#define INSTANTIATE_SUM_EVALUATIONS(HandlerTy) \ + \ + template std::string operator_sum::to_string() const; \ + \ + template matrix_2 operator_sum::to_matrix( \ + std::unordered_map dimensions, \ + const std::unordered_map> ¶ms) \ + const; INSTANTIATE_SUM_EVALUATIONS(matrix_operator); INSTANTIATE_SUM_EVALUATIONS(spin_operator); @@ -359,16 +386,14 @@ operator_sum operator_sum::operator+() const { return *this; } -#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ - \ - template \ - operator_sum operator_sum::operator-() const &; \ - \ - template \ - operator_sum operator_sum::operator-() &&; \ - \ - template \ - operator_sum operator_sum::operator+() const; +#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ + \ + template operator_sum operator_sum::operator-() \ + const &; \ + \ + template operator_sum operator_sum::operator-() &&; \ + \ + template operator_sum operator_sum::operator+() const; INSTANTIATE_SUM_UNARY_OPS(matrix_operator); INSTANTIATE_SUM_UNARY_OPS(spin_operator); @@ -376,26 +401,28 @@ INSTANTIATE_SUM_UNARY_OPS(boson_operator); // right-hand arithmetics -#define SUM_MULTIPLICATION(otherTy) \ - template \ - operator_sum operator_sum::operator*(otherTy other) const { \ - operator_sum sum; \ - sum.tmap.reserve(this->tmap.size()); \ - for (const auto &entry : this->tmap) \ - sum.insert(other * entry.second); \ - return sum; \ +#define SUM_MULTIPLICATION(otherTy) \ + template \ + operator_sum operator_sum::operator*(otherTy other) \ + const { \ + operator_sum sum; \ + sum.tmap.reserve(this->tmap.size()); \ + for (const auto &entry : this->tmap) \ + sum.insert(other *entry.second); \ + return sum; \ } SUM_MULTIPLICATION(double); SUM_MULTIPLICATION(std::complex); SUM_MULTIPLICATION(const scalar_operator &); -#define SUM_ADDITION(otherTy, op) \ - template \ - operator_sum operator_sum::operator op(otherTy other) const { \ - operator_sum sum(*this); \ - sum.insert(product_operator(op other)); \ - return sum; \ +#define SUM_ADDITION(otherTy, op) \ + template \ + operator_sum operator_sum::operator op(otherTy other) \ + const { \ + operator_sum sum(*this); \ + sum.insert(product_operator(op other)); \ + return sum; \ } SUM_ADDITION(double, +); @@ -405,33 +432,34 @@ SUM_ADDITION(std::complex, -); SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); -#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ - \ - template \ - operator_sum operator_sum::operator*(double other) const; \ - template \ - operator_sum operator_sum::operator+(double other) const; \ - template \ - operator_sum operator_sum::operator-(double other) const; \ - template \ - operator_sum operator_sum::operator*(std::complex other) const; \ - template \ - operator_sum operator_sum::operator+(std::complex other) const; \ - template \ - operator_sum operator_sum::operator-(std::complex other) const; \ - template \ - operator_sum operator_sum::operator*(const scalar_operator &other) const; \ - template \ - operator_sum operator_sum::operator+(const scalar_operator &other) const; \ - template \ - operator_sum operator_sum::operator-(const scalar_operator &other) const; +#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ + \ + template operator_sum operator_sum::operator*( \ + double other) const; \ + template operator_sum operator_sum::operator+( \ + double other) const; \ + template operator_sum operator_sum::operator-( \ + double other) const; \ + template operator_sum operator_sum::operator*( \ + std::complex other) const; \ + template operator_sum operator_sum::operator+( \ + std::complex other) const; \ + template operator_sum operator_sum::operator-( \ + std::complex other) const; \ + template operator_sum operator_sum::operator*( \ + const scalar_operator &other) const; \ + template operator_sum operator_sum::operator+( \ + const scalar_operator &other) const; \ + template operator_sum operator_sum::operator-( \ + const scalar_operator &other) const; INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template -operator_sum operator_sum::operator*(const product_operator &other) const { +operator_sum operator_sum::operator*( + const product_operator &other) const { operator_sum sum; sum.tmap.reserve(this->tmap.size()); for (const auto &entry : this->tmap) @@ -439,20 +467,21 @@ operator_sum operator_sum::operator*(const product_operato return sum; } -#define SUM_ADDITION_PRODUCT(op) \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) const { \ - operator_sum sum(*this); \ - sum.insert(op other); \ - return sum; \ +#define SUM_ADDITION_PRODUCT(op) \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const { \ + operator_sum sum(*this); \ + sum.insert(op other); \ + return sum; \ } SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum operator_sum::operator*(const operator_sum &other) const { +operator_sum +operator_sum::operator*(const operator_sum &other) const { operator_sum sum; sum.tmap.reserve(this->tmap.size() * other.tmap.size()); for (const auto &entry_self : this->tmap) { @@ -462,64 +491,60 @@ operator_sum operator_sum::operator*(const operator_sum \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) const { \ - operator_sum sum; \ - sum.tmap.reserve(this->tmap.size() + other.tmap.size()); \ - for (const auto &entry : this->tmap) \ - sum.tmap.insert(entry); \ - for (const auto &entry : other.tmap) \ - sum.insert(op entry.second); \ - return sum; \ +#define SUM_ADDITION_SUM(op) \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const { \ + operator_sum sum; \ + sum.tmap.reserve(this->tmap.size() + other.tmap.size()); \ + for (const auto &entry : this->tmap) \ + sum.tmap.insert(entry); \ + for (const auto &entry : other.tmap) \ + sum.insert(op entry.second); \ + return sum; \ } SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); -#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - operator_sum operator_sum::operator*( \ - const product_operator &other) const; \ - template \ - operator_sum operator_sum::operator+( \ - const product_operator &other) const; \ - template \ - operator_sum operator_sum::operator-( \ - const product_operator &other) const; \ - template \ - operator_sum operator_sum::operator*( \ - const operator_sum &other) const; \ - template \ - operator_sum operator_sum::operator+( \ - const operator_sum &other) const; \ - template \ - operator_sum operator_sum::operator-( \ - const operator_sum &other) const; +#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template operator_sum operator_sum::operator*( \ + const product_operator &other) const; \ + template operator_sum operator_sum::operator+( \ + const product_operator &other) const; \ + template operator_sum operator_sum::operator-( \ + const product_operator &other) const; \ + template operator_sum operator_sum::operator*( \ + const operator_sum &other) const; \ + template operator_sum operator_sum::operator+( \ + const operator_sum &other) const; \ + template operator_sum operator_sum::operator-( \ + const operator_sum &other) const; INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(boson_operator); -#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - operator_sum& operator_sum::operator*=(otherTy other) { \ - for (auto &entry : this->tmap) \ - entry.second.coefficient *= other; \ - return *this; \ +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum &operator_sum::operator*=( \ + otherTy other) { \ + for (auto &entry : this->tmap) \ + entry.second.coefficient *= other; \ + return *this; \ } SUM_MULTIPLICATION_ASSIGNMENT(double); SUM_MULTIPLICATION_ASSIGNMENT(std::complex); SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ - template \ - operator_sum& operator_sum::operator op##=(otherTy other) { \ - this->insert(product_operator(op other)); \ - return *this; \ +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + otherTy other) { \ + this->insert(product_operator(op other)); \ + return *this; \ } SUM_ADDITION_ASSIGNMENT(double, +); @@ -530,7 +555,8 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template -operator_sum& operator_sum::operator*=(const product_operator &other) { +operator_sum & +operator_sum::operator*=(const product_operator &other) { operator_sum sum; sum.tmap.reserve(this->tmap.size()); for (auto &entry : this->tmap) @@ -539,69 +565,70 @@ operator_sum& operator_sum::operator*=(const product_opera return *this; } -#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const product_operator &other) { \ - this->insert(op other); \ - return *this; \ +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const product_operator &other) { \ + this->insert(op other); \ + return *this; \ } SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template -operator_sum& operator_sum::operator*=(const operator_sum &other) { +operator_sum & +operator_sum::operator*=(const operator_sum &other) { this->tmap.reserve(this->tmap.size() * other.tmap.size()); *this = *this * other; // we need to update all entries anyway return *this; } -#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ - template \ - operator_sum& operator_sum::operator op##=( \ - const operator_sum &other) { \ - this->tmap.reserve(this->tmap.size() + other.tmap.size()); \ - for (const auto &entry : other.tmap) \ - this->insert(op entry.second); \ - return *this; \ +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + const operator_sum &other) { \ + this->tmap.reserve(this->tmap.size() + other.tmap.size()); \ + for (const auto &entry : other.tmap) \ + this->insert(op entry.second); \ + return *this; \ } SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); -#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ - \ - template \ - operator_sum& operator_sum::operator*=(double other); \ - template \ - operator_sum& operator_sum::operator+=(double other); \ - template \ - operator_sum& operator_sum::operator-=(double other); \ - template \ - operator_sum& operator_sum::operator*=(std::complex other); \ - template \ - operator_sum& operator_sum::operator+=(std::complex other); \ - template \ - operator_sum& operator_sum::operator-=(std::complex other); \ - template \ - operator_sum& operator_sum::operator*=(const scalar_operator &other); \ - template \ - operator_sum& operator_sum::operator+=(const scalar_operator &other); \ - template \ - operator_sum& operator_sum::operator-=(const scalar_operator &other); \ - template \ - operator_sum& operator_sum::operator*=(const product_operator &other); \ - template \ - operator_sum& operator_sum::operator+=(const product_operator &other); \ - template \ - operator_sum& operator_sum::operator-=(const product_operator &other); \ - template \ - operator_sum& operator_sum::operator*=(const operator_sum &other); \ - template \ - operator_sum& operator_sum::operator-=(const operator_sum &other); \ - template \ - operator_sum& operator_sum::operator+=(const operator_sum &other); +#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ + \ + template operator_sum &operator_sum::operator*=( \ + double other); \ + template operator_sum &operator_sum::operator+=( \ + double other); \ + template operator_sum &operator_sum::operator-=( \ + double other); \ + template operator_sum &operator_sum::operator*=( \ + std::complex other); \ + template operator_sum &operator_sum::operator+=( \ + std::complex other); \ + template operator_sum &operator_sum::operator-=( \ + std::complex other); \ + template operator_sum &operator_sum::operator*=( \ + const scalar_operator &other); \ + template operator_sum &operator_sum::operator+=( \ + const scalar_operator &other); \ + template operator_sum &operator_sum::operator-=( \ + const scalar_operator &other); \ + template operator_sum &operator_sum::operator*=( \ + const product_operator &other); \ + template operator_sum &operator_sum::operator+=( \ + const product_operator &other); \ + template operator_sum &operator_sum::operator-=( \ + const product_operator &other); \ + template operator_sum &operator_sum::operator*=( \ + const operator_sum &other); \ + template operator_sum &operator_sum::operator-=( \ + const operator_sum &other); \ + template operator_sum &operator_sum::operator+=( \ + const operator_sum &other); INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); @@ -609,33 +636,34 @@ INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define SUM_MULTIPLICATION_REVERSE(otherTy) \ - template \ - operator_sum operator*(otherTy other, \ - const operator_sum &self) { \ - operator_sum sum; \ - sum.tmap.reserve(self.tmap.size()); \ - for (auto entry : self.tmap) { \ - entry.second.coefficient *= other; \ - sum.tmap.insert(entry); \ - } \ - return sum; \ +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum; \ + sum.tmap.reserve(self.tmap.size()); \ + for (auto entry : self.tmap) { \ + entry.second.coefficient *= other; \ + sum.tmap.insert(entry); \ + } \ + return sum; \ } SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); -#define SUM_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const operator_sum &self) { \ - operator_sum sum; \ - sum.tmap.reserve(self.tmap.size() + 1); \ - for (auto entry : self.tmap) \ - sum.tmap.insert(std::make_pair(entry.first, op std::move(entry.second))); \ - sum.insert(product_operator(other)); \ - return sum; \ +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum; \ + sum.tmap.reserve(self.tmap.size() + 1); \ + for (auto entry : self.tmap) \ + sum.tmap.insert( \ + std::make_pair(entry.first, op std::move(entry.second))); \ + sum.insert(product_operator(other)); \ + return sum; \ } SUM_ADDITION_REVERSE(double, +); @@ -645,26 +673,26 @@ SUM_ADDITION_REVERSE(std::complex, -); SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); -#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - operator_sum operator*(double other, const operator_sum &self); \ - template \ - operator_sum operator+(double other, const operator_sum &self); \ - template \ - operator_sum operator-(double other, const operator_sum &self); \ - template \ - operator_sum operator*(std::complex other, const operator_sum &self); \ - template \ - operator_sum operator+(std::complex other, const operator_sum &self); \ - template \ - operator_sum operator-(std::complex other, const operator_sum &self); \ - template \ - operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ - template \ - operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ - template \ - operator_sum operator-(const scalar_operator &other, const operator_sum &self); +#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template operator_sum operator*( \ + double other, const operator_sum &self); \ + template operator_sum operator+( \ + double other, const operator_sum &self); \ + template operator_sum operator-( \ + double other, const operator_sum &self); \ + template operator_sum operator*( \ + std::complex other, const operator_sum &self); \ + template operator_sum operator+( \ + std::complex other, const operator_sum &self); \ + template operator_sum operator-( \ + std::complex other, const operator_sum &self); \ + template operator_sum operator*( \ + const scalar_operator &other, const operator_sum &self); \ + template operator_sum operator+( \ + const scalar_operator &other, const operator_sum &self); \ + template operator_sum operator-( \ + const scalar_operator &other, const operator_sum &self); INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); @@ -672,73 +700,75 @@ INSTANTIATE_SUM_LHCOMPOSITE_OPS(boson_operator); // arithmetics that require conversions -#define SUM_CONVERSIONS_OPS(op) \ - \ - template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self) { \ - return operator_sum(other) op self; \ - } \ - \ - template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self) { \ - return product_operator(other) op self; \ - } \ - \ - template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self) { \ - return operator_sum(other) op self; \ +#define SUM_CONVERSIONS_OPS(op) \ + \ + template \ + operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self) { \ + return operator_sum(other) op self; \ + } \ + \ + template \ + operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self) { \ + return product_operator(other) op self; \ + } \ + \ + template \ + operator_sum operator op( \ + const operator_sum &other, const operator_sum &self) { \ + return operator_sum(other) op self; \ } SUM_CONVERSIONS_OPS(*); SUM_CONVERSIONS_OPS(+); SUM_CONVERSIONS_OPS(-); -#define INSTANTIATE_SUM_CONVERSION_OPS(op) \ - \ - template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - \ - template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - \ - template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ - template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ - template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ - template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ +#define INSTANTIATE_SUM_CONVERSION_OPS(op) \ + \ + template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + \ + template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + \ + template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); \ + template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); \ + template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); \ + template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); INSTANTIATE_SUM_CONVERSION_OPS(*); INSTANTIATE_SUM_CONVERSION_OPS(+); @@ -755,9 +785,7 @@ template operator_sum operator_handler::empty(); template operator_sum operator_handler::empty(); template operator_sum operator_handler::empty(); - #ifdef CUDAQ_INSTANTIATE_TEMPLATES -template class operator_sum; template class operator_sum; template class operator_sum; #endif diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 57676a30c6..4492ac0256 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -9,25 +9,27 @@ #include #include #include -#include #include +#include +#include "boson_operators.h" #include "cudaq/operators.h" #include "helpers.h" #include "manipulation.h" #include "matrix_operators.h" #include "spin_operators.h" -#include "boson_operators.h" namespace cudaq { // private methods #if !defined(NDEBUG) -// check canonicalization by default, individual handlers can set it to false to disable the check +// check canonicalization by default, individual handlers can set it to false to +// disable the check bool operator_handler::can_be_canonicalized = true; -// returns true if and only if applying the operators in sequence acts only once on each degree of freedom and in canonical order +// returns true if and only if applying the operators in sequence acts only once +// on each degree of freedom and in canonical order template bool product_operator::is_canonicalized() const { auto canon_degrees = this->degrees(); @@ -41,54 +43,77 @@ bool product_operator::is_canonicalized() const { } #endif -template -std::vector::const_iterator product_operator::find_insert_at(const HandlerTy &other) const { - // the logic below just ensures that terms are fully or partially ordered in canonical order - - // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers - return std::find_if(this->operators.crbegin(), this->operators.crend(), - [&other_degrees = static_cast&>(other.degrees())] - (const HandlerTy& self_op) { - const std::vector &self_op_degrees = self_op.degrees(); - for (auto other_degree : other_degrees) { // fixme: special case on single qubit handlers instead? - auto item_it = std::find_if(self_op_degrees.crbegin(), self_op_degrees.crend(), - [other_degree](int self_degree) { return other_degree <= self_degree; }); // FIXME: relies on canonical order - if (item_it != self_op_degrees.crend()) return true; - } - return false; - }).base(); // base causes insert after for reverse iterator +template +std::vector::const_iterator +product_operator::find_insert_at(const HandlerTy &other) const { + // the logic below just ensures that terms are fully or partially ordered in + // canonical order - a best effort is made to order terms, but a full + // canonical ordering is not possible for certain handlers + return std::find_if( + this->operators.crbegin(), this->operators.crend(), + [&other_degrees = static_cast &>( + other.degrees())](const HandlerTy &self_op) { + const std::vector &self_op_degrees = self_op.degrees(); + for (auto other_degree : + other_degrees) { // fixme: special case on single qubit + // handlers instead? + auto item_it = std::find_if( + self_op_degrees.crbegin(), self_op_degrees.crend(), + [other_degree](int self_degree) { + return other_degree <= self_degree; + }); // FIXME: relies on canonical order + if (item_it != self_op_degrees.crend()) + return true; + } + return false; + }) + .base(); // base causes insert after for reverse iterator } -template -template::value && !product_operator::supports_inplace_mult, int>> -void product_operator::insert(T &&other, bool update_id) { +template +template ::value && + !product_operator::supports_inplace_mult, + int>> +void product_operator::insert(T &&other, bool update_id) { auto pos = this->find_insert_at(other); this->operators.insert(pos, other); - if (update_id) this->update_id(); + if (update_id) + this->update_id(); } -template -template ::value && product_operator::supports_inplace_mult, bool>> +template +template ::value && + product_operator::supports_inplace_mult, + bool>> void product_operator::insert(T &&other, bool update_id) { auto pos = this->find_insert_at(other); - if (pos != this->operators.begin() && (pos - 1)->target == other.target) - this->coefficient *= this->operators.erase(pos - 1, pos - 1)->inplace_mult(other); // erase: constant time conversion to non-const iterator - else this->operators.insert(pos, std::move(other)); - if (update_id) this->update_id(); + if (pos != this->operators.begin() && (pos - 1)->target == other.target) + this->coefficient *= + this->operators.erase(pos - 1, pos - 1) + ->inplace_mult( + other); // erase: constant time conversion to non-const iterator + else + this->operators.insert(pos, std::move(other)); + if (update_id) + this->update_id(); } -template +template void product_operator::update_id() { this->term_id = ""; for (const auto &op : this->operators) this->term_id += op.unique_id(); } -template +template void product_operator::aggregate_terms() {} -template -template -void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... args) { +template +template +void product_operator::aggregate_terms(HandlerTy &&head, + Args &&...args) { this->insert(std::forward(head), false); aggregate_terms(std::forward(args)...); } @@ -96,27 +121,36 @@ void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... a // FIXME: EVALUATE IS NOT SUPPOSED TO RETURN A MATRIX - // IT SUPPOSED TO TAKE A TRANSFORMATION (ANY OPERATOR ARITHMETICS) AND APPLY IT template -EvaluatedMatrix product_operator::m_evaluate( - MatrixArithmetics arithmetics, bool pad_terms) const { +EvaluatedMatrix +product_operator::m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms) const { auto degrees = this->degrees(); cudaq::matrix_2 result; - auto padded_op = [&arithmetics, °rees = std::as_const(degrees)](const HandlerTy &op) { - std::vector padded; - auto op_degrees = op.degrees(); - for (const auto °ree : degrees) { - if (std::find(op_degrees.cbegin(), op_degrees.cend(), degree) == op_degrees.cend()) { - auto identity = HandlerTy(degree); - padded.push_back(EvaluatedMatrix(identity.degrees(), identity.to_matrix(arithmetics.m_dimensions))); - } + auto padded_op = [&arithmetics, + °rees = std::as_const(degrees)](const HandlerTy &op) { + std::vector padded; + auto op_degrees = op.degrees(); + for (const auto °ree : degrees) { + if (std::find(op_degrees.cbegin(), op_degrees.cend(), degree) == + op_degrees.cend()) { + auto identity = HandlerTy(degree); + padded.push_back(EvaluatedMatrix( + identity.degrees(), identity.to_matrix(arithmetics.m_dimensions))); } - /// Creating the tensor product with op being last is most efficient. - if (padded.size() == 0) - return EvaluatedMatrix(op_degrees, op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); - EvaluatedMatrix ids(std::move(padded[0])); - for (auto i = 1; i < padded.size(); ++i) - ids = arithmetics.tensor(std::move(ids), std::move(padded[i])); - return arithmetics.tensor(std::move(ids), EvaluatedMatrix(op_degrees, op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters))); + } + /// Creating the tensor product with op being last is most efficient. + if (padded.size() == 0) + return EvaluatedMatrix( + op_degrees, + op.to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); + EvaluatedMatrix ids(std::move(padded[0])); + for (auto i = 1; i < padded.size(); ++i) + ids = arithmetics.tensor(std::move(ids), std::move(padded[i])); + return arithmetics.tensor( + std::move(ids), + EvaluatedMatrix(op_degrees, op.to_matrix(arithmetics.m_dimensions, + arithmetics.m_parameters))); }; auto coefficient = this->coefficient.evaluate(arithmetics.m_parameters); @@ -125,17 +159,27 @@ EvaluatedMatrix product_operator::m_evaluate( EvaluatedMatrix prod = padded_op(this->operators[0]); for (auto op_idx = 1; op_idx < this->operators.size(); ++op_idx) { auto op_degrees = this->operators[op_idx].degrees(); - if (op_degrees.size() != 1 || this->operators[op_idx] != HandlerTy(op_degrees[0])) - prod = arithmetics.mul(std::move(prod), padded_op(this->operators[op_idx])); + if (op_degrees.size() != 1 || + this->operators[op_idx] != HandlerTy(op_degrees[0])) + prod = arithmetics.mul(std::move(prod), + padded_op(this->operators[op_idx])); } - return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); + return EvaluatedMatrix(std::move(prod.degrees()), + coefficient * prod.matrix()); } else { - EvaluatedMatrix prod(this->operators[0].degrees(), this->operators[0].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters)); + EvaluatedMatrix prod( + this->operators[0].degrees(), + this->operators[0].to_matrix(arithmetics.m_dimensions, + arithmetics.m_parameters)); for (auto op_idx = 1; op_idx < this->operators.size(); ++op_idx) { - auto mat = this->operators[op_idx].to_matrix(arithmetics.m_dimensions, arithmetics.m_parameters); - prod = arithmetics.mul(std::move(prod), EvaluatedMatrix(this->operators[op_idx].degrees(), mat)); + auto mat = this->operators[op_idx].to_matrix(arithmetics.m_dimensions, + arithmetics.m_parameters); + prod = arithmetics.mul( + std::move(prod), + EvaluatedMatrix(this->operators[op_idx].degrees(), mat)); } - return EvaluatedMatrix(std::move(prod.degrees()), coefficient * prod.matrix()); + return EvaluatedMatrix(std::move(prod.degrees()), + coefficient * prod.matrix()); } } else { assert(degrees.size() == 0); // degrees are stored with each term @@ -143,19 +187,15 @@ EvaluatedMatrix product_operator::m_evaluate( } } -#define INSTANTIATE_PRODUCT_PRIVATE_METHODS(HandlerTy) \ - \ - template \ - void product_operator::aggregate_terms(HandlerTy &&item1, \ - HandlerTy &&item2); \ - \ - template \ - void product_operator::aggregate_terms(HandlerTy &&item1, \ - HandlerTy &&item2, \ - HandlerTy &&item3); \ - \ - template \ - EvaluatedMatrix product_operator::m_evaluate( \ +#define INSTANTIATE_PRODUCT_PRIVATE_METHODS(HandlerTy) \ + \ + template void product_operator::aggregate_terms( \ + HandlerTy &&item1, HandlerTy &&item2); \ + \ + template void product_operator::aggregate_terms( \ + HandlerTy &&item1, HandlerTy &&item2, HandlerTy &&item3); \ + \ + template EvaluatedMatrix product_operator::m_evaluate( \ MatrixArithmetics arithmetics, bool pad_terms) const; INSTANTIATE_PRODUCT_PRIVATE_METHODS(matrix_operator); @@ -171,39 +211,37 @@ std::vector product_operator::degrees() const { auto term_degrees = term.degrees(); unsorted_degrees.insert(term_degrees.cbegin(), term_degrees.cend()); } - auto degrees = std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); + auto degrees = + std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); cudaq::detail::canonicalize_degrees(degrees); return degrees; } -template -int product_operator::num_terms() const { - return this->operators.size(); +template +int product_operator::num_terms() const { + return this->operators.size(); } -template -const std::vector& product_operator::get_terms() const { - return this->operators; +template +const std::vector &product_operator::get_terms() const { + return this->operators; } -template -scalar_operator product_operator::get_coefficient() const { - return this->coefficient; +template +scalar_operator product_operator::get_coefficient() const { + return this->coefficient; } -#define INSTANTIATE_PRODUCT_PROPERTIES(HandlerTy) \ - \ - template \ - std::vector product_operator::degrees() const; \ - \ - template \ - int product_operator::num_terms() const; \ - \ - template \ - const std::vector& product_operator::get_terms() const; \ - \ - template \ - scalar_operator product_operator::get_coefficient() const; +#define INSTANTIATE_PRODUCT_PROPERTIES(HandlerTy) \ + \ + template std::vector product_operator::degrees() const; \ + \ + template int product_operator::num_terms() const; \ + \ + template const std::vector & \ + product_operator::get_terms() const; \ + \ + template scalar_operator product_operator::get_coefficient() const; INSTANTIATE_PRODUCT_PROPERTIES(matrix_operator); INSTANTIATE_PRODUCT_PROPERTIES(spin_operator); @@ -211,45 +249,59 @@ INSTANTIATE_PRODUCT_PROPERTIES(boson_operator); // constructors -template +template product_operator::product_operator(double coefficient) - : coefficient(coefficient), term_id() {} + : coefficient(coefficient), term_id() {} -template +template product_operator::product_operator(HandlerTy &&atomic) - : coefficient(1.), term_id(atomic.unique_id()) { + : coefficient(1.), term_id(atomic.unique_id()) { this->operators.push_back(std::move(atomic)); - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); // relevant for custom matrix operators acting on multiple degrees of freedom + assert(!HandlerTy::can_be_canonicalized || + this->is_canonicalized()); // relevant for custom matrix operators + // acting on multiple degrees of freedom } -template -template...>::value, bool>> -product_operator::product_operator(scalar_operator coefficient, Args&&... args) - : coefficient(std::move(coefficient)) { +template +template ...>::value, bool>> +product_operator::product_operator(scalar_operator coefficient, + Args &&...args) + : coefficient(std::move(coefficient)) { this->operators.reserve(sizeof...(Args)); aggregate_terms(std::forward(args)...); this->update_id(); - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); + assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) -template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators, const std::string &term_id) - : coefficient(std::move(coefficient)), operators(atomic_operators), term_id(term_id) { - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); +template +product_operator::product_operator( + scalar_operator coefficient, const std::vector &atomic_operators, + const std::string &term_id) + : coefficient(std::move(coefficient)), operators(atomic_operators), + term_id(term_id) { + assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) -template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators, std::string &&term_id) - : coefficient(std::move(coefficient)), operators(std::move(atomic_operators)), term_id(std::move(term_id)) { - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); +template +product_operator::product_operator( + scalar_operator coefficient, std::vector &&atomic_operators, + std::string &&term_id) + : coefficient(std::move(coefficient)), + operators(std::move(atomic_operators)), term_id(std::move(term_id)) { + assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } -template -template::value && std::is_constructible::value, bool>> -product_operator::product_operator(const product_operator &other) - : coefficient(other.coefficient) { +template +template ::value && + std::is_constructible::value, + bool>> +product_operator::product_operator(const product_operator &other) + : coefficient(other.coefficient) { for (const T &other_op : other.operators) { HandlerTy op(other_op); this->operators.push_back(op); @@ -257,64 +309,59 @@ product_operator::product_operator(const product_operator &other) } } -template -product_operator::product_operator(const product_operator &other) - : coefficient(other.coefficient), term_id(other.term_id) { +template +product_operator::product_operator( + const product_operator &other) + : coefficient(other.coefficient), term_id(other.term_id) { this->operators = other.operators; } -template -product_operator::product_operator(product_operator &&other) - : coefficient(std::move(other.coefficient)), term_id(std::move(other.term_id)) { +template +product_operator::product_operator( + product_operator &&other) + : coefficient(std::move(other.coefficient)), + term_id(std::move(other.term_id)) { this->operators = std::move(other.operators); } -#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ - \ - template \ - product_operator::product_operator(double coefficient); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient); \ - \ - template \ - product_operator::product_operator(HandlerTy &&atomic); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - HandlerTy &&atomic1); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - HandlerTy &&atomic1, \ - HandlerTy &&atomic2); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - HandlerTy &&atomic1, \ - HandlerTy &&atomic2, \ - HandlerTy &&atomic3); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - const std::vector &atomic_operators, const std::string &term_id); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - std::vector &&atomic_operators, std::string &&term_id); \ - \ - template \ - product_operator::product_operator( \ - const product_operator &other); \ - \ - template \ - product_operator::product_operator( \ - product_operator &&other); - -template -product_operator::product_operator(const product_operator &other); -template -product_operator::product_operator(const product_operator &other); +#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ + \ + template product_operator::product_operator(double coefficient); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient); \ + \ + template product_operator::product_operator(HandlerTy &&atomic); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, HandlerTy &&atomic1); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, HandlerTy &&atomic1, HandlerTy &&atomic2); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, HandlerTy &&atomic1, HandlerTy &&atomic2, \ + HandlerTy &&atomic3); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, \ + const std::vector &atomic_operators, \ + const std::string &term_id); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, std::vector &&atomic_operators, \ + std::string &&term_id); \ + \ + template product_operator::product_operator( \ + const product_operator &other); \ + \ + template product_operator::product_operator( \ + product_operator &&other); + +template product_operator::product_operator( + const product_operator &other); +template product_operator::product_operator( + const product_operator &other); INSTANTIATE_PRODUCT_CONSTRUCTORS(matrix_operator); INSTANTIATE_PRODUCT_CONSTRUCTORS(spin_operator); @@ -322,15 +369,20 @@ INSTANTIATE_PRODUCT_CONSTRUCTORS(boson_operator); // assignments -template -template::value && std::is_constructible::value, bool>> -product_operator& product_operator::operator=(const product_operator &other) { +template +template ::value && + std::is_constructible::value, + bool>> +product_operator & +product_operator::operator=(const product_operator &other) { *this = product_operator(other); return *this; } -template -product_operator& product_operator::operator=(const product_operator &other) { +template +product_operator &product_operator::operator=( + const product_operator &other) { if (this != &other) { this->coefficient = other.coefficient; this->operators = other.operators; @@ -350,20 +402,21 @@ product_operator::operator=(product_operator &&other) { return *this; } -#define INSTANTIATE_PRODUCT_ASSIGNMENTS(HandlerTy) \ - \ - template \ - product_operator& product_operator::operator=( \ - const product_operator &other); \ - \ - template \ - product_operator& product_operator::operator=( \ - product_operator &&other); - -template -product_operator& product_operator::operator=(const product_operator &other); -template -product_operator& product_operator::operator=(const product_operator &other); +#define INSTANTIATE_PRODUCT_ASSIGNMENTS(HandlerTy) \ + \ + template product_operator & \ + product_operator::operator=( \ + const product_operator &other); \ + \ + template product_operator & \ + product_operator::operator=(product_operator &&other); + +template product_operator & +product_operator::operator=( + const product_operator &other); +template product_operator & +product_operator::operator=( + const product_operator &other); INSTANTIATE_PRODUCT_ASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_ASSIGNMENTS(spin_operator); @@ -376,21 +429,22 @@ std::string product_operator::to_string() const { return this->term_id; } -template -matrix_2 product_operator::to_matrix(std::unordered_map dimensions, - const std::unordered_map> ¶meters) const { +template +matrix_2 product_operator::to_matrix( + std::unordered_map dimensions, + const std::unordered_map> ¶meters) + const { return this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); } -#define INSTANTIATE_PRODUCT_EVALUATIONS(HandlerTy) \ - \ - template \ - std::string product_operator::to_string() const; \ - \ - template \ - matrix_2 product_operator::to_matrix( \ - std::unordered_map dimensions, \ - const std::unordered_map> ¶meters) const; +#define INSTANTIATE_PRODUCT_EVALUATIONS(HandlerTy) \ + \ + template std::string product_operator::to_string() const; \ + \ + template matrix_2 product_operator::to_matrix( \ + std::unordered_map dimensions, \ + const std::unordered_map> ¶meters) \ + const; INSTANTIATE_PRODUCT_EVALUATIONS(matrix_operator); INSTANTIATE_PRODUCT_EVALUATIONS(spin_operator); @@ -398,16 +452,17 @@ INSTANTIATE_PRODUCT_EVALUATIONS(boson_operator); // comparisons -template -bool product_operator::operator==(const product_operator &other) const { - return this->term_id == other.term_id && this->coefficient == other.coefficient; +template +bool product_operator::operator==( + const product_operator &other) const { + return this->term_id == other.term_id && + this->coefficient == other.coefficient; } -#define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ - \ - template \ - bool product_operator::operator==( \ - const product_operator &other) const; +#define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ + \ + template bool product_operator::operator==( \ + const product_operator &other) const; INSTANTIATE_PRODUCT_COMPARISONS(matrix_operator); INSTANTIATE_PRODUCT_COMPARISONS(spin_operator); @@ -417,7 +472,8 @@ INSTANTIATE_PRODUCT_COMPARISONS(boson_operator); template product_operator product_operator::operator-() const & { - return product_operator(-1. * this->coefficient, this->operators, this->term_id); + return product_operator(-1. * this->coefficient, this->operators, + this->term_id); } template @@ -431,16 +487,16 @@ product_operator product_operator::operator+() const { return *this; } -#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ - \ - template \ - product_operator product_operator::operator-() const &; \ - \ - template \ - product_operator product_operator::operator-() &&; \ - \ - template \ - product_operator product_operator::operator+() const; +#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ + \ + template product_operator \ + product_operator::operator-() const &; \ + \ + template product_operator \ + product_operator::operator-() &&; \ + \ + template product_operator \ + product_operator::operator+() const; INSTANTIATE_PRODUCT_UNARY_OPS(matrix_operator); INSTANTIATE_PRODUCT_UNARY_OPS(spin_operator); @@ -448,24 +504,24 @@ INSTANTIATE_PRODUCT_UNARY_OPS(boson_operator); // right-hand arithmetics -#define PRODUCT_MULTIPLICATION(otherTy) \ - template \ - product_operator product_operator::operator*( \ - otherTy other) const { \ - return product_operator(other * this->coefficient, \ - this->operators, this->term_id); \ +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficient, \ + this->operators, this->term_id); \ } PRODUCT_MULTIPLICATION(double); PRODUCT_MULTIPLICATION(std::complex); PRODUCT_MULTIPLICATION(const scalar_operator &); -#define PRODUCT_ADDITION(otherTy, op) \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) const { \ - return operator_sum(product_operator(op other), \ - product_operator(*this)); \ +#define PRODUCT_ADDITION(otherTy, op) \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const { \ + return operator_sum(product_operator(op other), \ + product_operator(*this)); \ } PRODUCT_ADDITION(double, +); @@ -475,26 +531,26 @@ PRODUCT_ADDITION(std::complex, -); PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); -#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ - \ - template \ - product_operator product_operator::operator*(double other) const; \ - template \ - operator_sum product_operator::operator+(double other) const; \ - template \ - operator_sum product_operator::operator-(double other) const; \ - template \ - product_operator product_operator::operator*(std::complex other) const; \ - template \ - operator_sum product_operator::operator+(std::complex other) const; \ - template \ - operator_sum product_operator::operator-(std::complex other) const; \ - template \ - product_operator product_operator::operator*(const scalar_operator &other) const; \ - template \ - operator_sum product_operator::operator+(const scalar_operator &other) const; \ - template \ - operator_sum product_operator::operator-(const scalar_operator &other) const; +#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ + \ + template product_operator product_operator::operator*( \ + double other) const; \ + template operator_sum product_operator::operator+( \ + double other) const; \ + template operator_sum product_operator::operator-( \ + double other) const; \ + template product_operator product_operator::operator*( \ + std::complex other) const; \ + template operator_sum product_operator::operator+( \ + std::complex other) const; \ + template operator_sum product_operator::operator-( \ + std::complex other) const; \ + template product_operator product_operator::operator*( \ + const scalar_operator &other) const; \ + template operator_sum product_operator::operator+( \ + const scalar_operator &other) const; \ + template operator_sum product_operator::operator-( \ + const scalar_operator &other) const; INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); @@ -507,22 +563,25 @@ product_operator product_operator::operator*( terms.reserve(this->operators.size() + other.operators.size()); for (auto &term : this->operators) terms.push_back(term); - product_operator self(this->coefficient, std::move(terms), this->term_id); + product_operator self(this->coefficient, std::move(terms), + this->term_id); return self *= other; } -#define PRODUCT_ADDITION_PRODUCT(op) \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) const { \ - return operator_sum(op other, product_operator(*this)); \ +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(op other, \ + product_operator(*this)); \ } PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) template -operator_sum product_operator::operator*(const operator_sum &other) const { +operator_sum product_operator::operator*( + const operator_sum &other) const { operator_sum sum; sum.tmap.reserve(other.tmap.size()); for (const auto &entry : other.tmap) @@ -530,51 +589,46 @@ operator_sum product_operator::operator*(const operator_su return sum; } -#define PRODUCT_ADDITION_SUM(op) \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) const { \ - operator_sum sum; \ - sum.tmap.reserve(other.tmap.size() + 1); \ - for (auto entry : other.tmap) /* copy here so pair doesn't copy */ \ - sum.tmap.insert(std::make_pair(entry.first, op entry.second)); \ - sum.insert(*this); \ - return sum; \ +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + operator_sum sum; \ + sum.tmap.reserve(other.tmap.size() + 1); \ + for (auto entry : other.tmap) /* copy here so pair doesn't copy */ \ + sum.tmap.insert(std::make_pair(entry.first, op entry.second)); \ + sum.insert(*this); \ + return sum; \ } PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) -#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - product_operator product_operator::operator*( \ - const product_operator &other) const; \ - template \ - operator_sum product_operator::operator+( \ - const product_operator &other) const; \ - template \ - operator_sum product_operator::operator-( \ - const product_operator &other) const; \ - template \ - operator_sum product_operator::operator*( \ - const operator_sum &other) const; \ - template \ - operator_sum product_operator::operator+( \ - const operator_sum &other) const; \ - template \ - operator_sum product_operator::operator-( \ - const operator_sum &other) const; +#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template product_operator product_operator::operator*( \ + const product_operator &other) const; \ + template operator_sum product_operator::operator+( \ + const product_operator &other) const; \ + template operator_sum product_operator::operator-( \ + const product_operator &other) const; \ + template operator_sum product_operator::operator*( \ + const operator_sum &other) const; \ + template operator_sum product_operator::operator+( \ + const operator_sum &other) const; \ + template operator_sum product_operator::operator-( \ + const operator_sum &other) const; INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(spin_operator); INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(boson_operator); -#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - product_operator& product_operator::operator*=(otherTy other) { \ - this->coefficient *= other; \ - return *this; \ +#define PRODUCT_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + product_operator &product_operator::operator*=( \ + otherTy other) { \ + this->coefficient *= other; \ + return *this; \ } PRODUCT_MULTIPLICATION_ASSIGNMENT(double); @@ -582,7 +636,8 @@ PRODUCT_MULTIPLICATION_ASSIGNMENT(std::complex); PRODUCT_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); template -product_operator& product_operator::operator*=(const product_operator &other) { +product_operator &product_operator::operator*=( + const product_operator &other) { this->coefficient *= other.coefficient; this->operators.reserve(this->operators.size() + other.operators.size()); for (HandlerTy other_op : other.operators) @@ -591,16 +646,17 @@ product_operator& product_operator::operator*=(const produ return *this; } -#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ - \ - template \ - product_operator& product_operator::operator*=(double other); \ - template \ - product_operator& product_operator::operator*=(std::complex other); \ - template \ - product_operator& product_operator::operator*=(const scalar_operator &other); \ - template \ - product_operator& product_operator::operator*=(const product_operator &other); +#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ + \ + template product_operator & \ + product_operator::operator*=(double other); \ + template product_operator & \ + product_operator::operator*=(std::complex other); \ + template product_operator & \ + product_operator::operator*=(const scalar_operator &other); \ + template product_operator & \ + product_operator::operator*=( \ + const product_operator &other); INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); @@ -608,12 +664,12 @@ INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ - template \ - product_operator operator*(otherTy other, \ - const product_operator &self) { \ - return product_operator(other * self.coefficient, \ - self.operators, self.term_id); \ +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*( \ + otherTy other, const product_operator &self) { \ + return product_operator(other * self.coefficient, \ + self.operators, self.term_id); \ } PRODUCT_MULTIPLICATION_REVERSE(double); @@ -635,26 +691,26 @@ PRODUCT_ADDITION_REVERSE(std::complex, -); PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); -#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - product_operator operator*(double other, const product_operator &self); \ - template \ - operator_sum operator+(double other, const product_operator &self); \ - template \ - operator_sum operator-(double other, const product_operator &self); \ - template \ - product_operator operator*(std::complex other, const product_operator &self); \ - template \ - operator_sum operator+(std::complex other, const product_operator &self); \ - template \ - operator_sum operator-(std::complex other, const product_operator &self); \ - template \ - product_operator operator*(const scalar_operator &other, const product_operator &self); \ - template \ - operator_sum operator+(const scalar_operator &other, const product_operator &self); \ - template \ - operator_sum operator-(const scalar_operator &other, const product_operator &self); +#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template product_operator operator*( \ + double other, const product_operator &self); \ + template operator_sum operator+( \ + double other, const product_operator &self); \ + template operator_sum operator-( \ + double other, const product_operator &self); \ + template product_operator operator*( \ + std::complex other, const product_operator &self); \ + template operator_sum operator+( \ + std::complex other, const product_operator &self); \ + template operator_sum operator-( \ + std::complex other, const product_operator &self); \ + template product_operator operator*( \ + const scalar_operator &other, const product_operator &self); \ + template operator_sum operator+( \ + const scalar_operator &other, const product_operator &self); \ + template operator_sum operator-( \ + const scalar_operator &other, const product_operator &self); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); @@ -662,32 +718,33 @@ INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(boson_operator); // arithmetics that require conversions -#define PRODUCT_CONVERSIONS_OPS(op, returnTy) \ - template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self) { \ - return product_operator(other) op self; \ +#define PRODUCT_CONVERSIONS_OPS(op, returnTy) \ + template \ + returnTy operator op( \ + const product_operator &other, \ + const product_operator &self) { \ + return product_operator(other) op self; \ } PRODUCT_CONVERSIONS_OPS(*, product_operator); PRODUCT_CONVERSIONS_OPS(+, operator_sum); PRODUCT_CONVERSIONS_OPS(-, operator_sum); -#define INSTANTIATE_PRODUCT_CONVERSION_OPS(op, returnTy) \ - \ - template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); +#define INSTANTIATE_PRODUCT_CONVERSION_OPS(op, returnTy) \ + \ + template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); INSTANTIATE_PRODUCT_CONVERSION_OPS(*, product_operator); INSTANTIATE_PRODUCT_CONVERSION_OPS(+, operator_sum); @@ -695,9 +752,14 @@ INSTANTIATE_PRODUCT_CONVERSION_OPS(-, operator_sum); // common operators -template...>::value, bool> = true> +template ...>::value, + bool> = true> product_operator operator_handler::identity(Args... targets) { - static_assert (std::is_constructible_v, "operator handlers must have a constructor that take a single degree of freedom and returns the identity operator on that degree."); + static_assert( + std::is_constructible_v, + "operator handlers must have a constructor that take a single degree of " + "freedom and returns the identity operator on that degree."); return product_operator(1.0, HandlerTy(targets)...); } @@ -705,13 +767,13 @@ template product_operator operator_handler::identity(); template product_operator operator_handler::identity(); template product_operator operator_handler::identity(); -template product_operator operator_handler::identity(int target); +template product_operator +operator_handler::identity(int target); template product_operator operator_handler::identity(int target); -template product_operator operator_handler::identity(int target); - +template product_operator +operator_handler::identity(int target); #ifdef CUDAQ_INSTANTIATE_TEMPLATES -template class product_operator; template class product_operator; template class product_operator; #endif diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 335558ec52..01737665ed 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -14,34 +14,41 @@ namespace cudaq { // constructors and destructors +bool scalar_operator::is_constant() const { + return std::holds_alternative>(value); +} -scalar_operator::scalar_operator(double value) - : value(std::variant, ScalarCallbackFunction>(std::complex(value))) {} +scalar_operator::scalar_operator(double value) + : value(std::variant, ScalarCallbackFunction>( + std::complex(value))) {} -scalar_operator::scalar_operator(std::complex value) - : value(std::variant, ScalarCallbackFunction>(value)) {} +scalar_operator::scalar_operator(std::complex value) + : value(std::variant, ScalarCallbackFunction>(value)) { +} -scalar_operator::scalar_operator(const ScalarCallbackFunction &create) - : value(std::variant, ScalarCallbackFunction>(create)) {} +scalar_operator::scalar_operator(const ScalarCallbackFunction &create) + : value( + std::variant, ScalarCallbackFunction>(create)) {} scalar_operator::scalar_operator(ScalarCallbackFunction &&create) - : value(std::variant, ScalarCallbackFunction>(std::move(create))) {} + : value(std::variant, ScalarCallbackFunction>( + std::move(create))) {} -scalar_operator::scalar_operator(const scalar_operator &other) - : value(other.value) {} +scalar_operator::scalar_operator(const scalar_operator &other) + : value(other.value) {} -scalar_operator::scalar_operator(scalar_operator &&other) - : value(std::move(other.value)) {} +scalar_operator::scalar_operator(scalar_operator &&other) + : value(std::move(other.value)) {} // assignments -scalar_operator& scalar_operator::operator=(const scalar_operator &other) { +scalar_operator &scalar_operator::operator=(const scalar_operator &other) { if (this != &other) this->value = other.value; return *this; } -scalar_operator& scalar_operator::operator=(scalar_operator &&other) { +scalar_operator &scalar_operator::operator=(scalar_operator &&other) { if (this != &other) this->value = std::move(other.value); return *this; @@ -50,14 +57,16 @@ scalar_operator& scalar_operator::operator=(scalar_operator &&other) { // evaluations std::complex scalar_operator::evaluate( - const std::unordered_map> ¶meters) const { - if (std::holds_alternative(this->value)) + const std::unordered_map> ¶meters) + const { + if (std::holds_alternative(this->value)) return std::get(this->value)(parameters); return std::get>(this->value); } matrix_2 scalar_operator::to_matrix( - const std::unordered_map> ¶meters) const { + const std::unordered_map> ¶meters) + const { auto returnOperator = matrix_2(1, 1); returnOperator[{0, 0}] = evaluate(parameters); return returnOperator; @@ -68,10 +77,12 @@ matrix_2 scalar_operator::to_matrix( bool scalar_operator::operator==(scalar_operator other) const { if (std::holds_alternative(this->value)) { return std::holds_alternative(other.value) && - &std::get(this->value) == &std::get(other.value); + &std::get(this->value) == + &std::get(other.value); } else { return std::holds_alternative>(this->value) && - std::get>(this->value) == std::get>(other.value); + std::get>(this->value) == + std::get>(other.value); } } @@ -83,18 +94,17 @@ scalar_operator scalar_operator::operator+() const { return *this; } // right-hand arithmetics -#define ARITHMETIC_OPERATIONS(op, otherTy) \ - scalar_operator scalar_operator::operator op(otherTy other) const { \ - if (std::holds_alternative>(this->value)) { \ - return scalar_operator( \ - std::get>(this->value) op other); \ - } \ - auto newGenerator = \ - [other, generator = std::get(this->value)]( \ - const std::unordered_map> ¶meters) { \ - return generator(parameters) op other; \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS(op, otherTy) \ + scalar_operator scalar_operator::operator op(otherTy other) const { \ + if (std::holds_alternative>(this->value)) { \ + return scalar_operator(std::get>(this->value) \ + op other); \ + } \ + auto newGenerator = \ + [other, generator = std::get(this->value)]( \ + const std::unordered_map> \ + ¶meters) { return generator(parameters) op other; }; \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS(*, double); @@ -106,21 +116,21 @@ ARITHMETIC_OPERATIONS(/, std::complex); ARITHMETIC_OPERATIONS(+, std::complex); ARITHMETIC_OPERATIONS(-, std::complex); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op( \ - const scalar_operator &other) const { \ - if (std::holds_alternative>(this->value) && \ - std::holds_alternative>(other.value)) { \ - return scalar_operator( \ - std::get>(this->value) op \ - std::get>(other.value)); \ - } \ - auto newGenerator = \ - [other, *this]( \ - const std::unordered_map> ¶meters) { \ - return this->evaluate(parameters) op other.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator scalar_operator::operator op(const scalar_operator &other) \ + const { \ + if (std::holds_alternative>(this->value) && \ + std::holds_alternative>(other.value)) { \ + return scalar_operator(std::get>( \ + this->value) op std::get>(other.value)); \ + } \ + auto newGenerator = \ + [other, \ + *this](const std::unordered_map> \ + ¶meters) { \ + return this->evaluate(parameters) op other.evaluate(parameters); \ + }; \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_SCALAR_OPS(*); @@ -128,19 +138,19 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(/); ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ - scalar_operator& scalar_operator::operator op##=(otherTy other) { \ - if (std::holds_alternative>(this->value)) { \ - this->value = std::get>(this->value) op other; \ - return *this; \ - } \ - auto newGenerator = \ - [other, generator = std::move(std::get(this->value))]( \ - const std::unordered_map> ¶meters) { \ - return generator(parameters) op##= other; \ - }; \ - this->value = newGenerator; \ - return *this; \ +#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ + scalar_operator &scalar_operator::operator op##=(otherTy other) { \ + if (std::holds_alternative>(this->value)) { \ + this->value = std::get>(this->value) op other; \ + return *this; \ + } \ + auto newGenerator = \ + [other, generator = \ + std::move(std::get(this->value))]( \ + const std::unordered_map> \ + ¶meters) { return generator(parameters) op## = other; }; \ + this->value = newGenerator; \ + return *this; \ } ARITHMETIC_OPERATIONS_ASSIGNMENT(*, double); @@ -152,23 +162,23 @@ ARITHMETIC_OPERATIONS_ASSIGNMENT(/, std::complex); ARITHMETIC_OPERATIONS_ASSIGNMENT(+, std::complex); ARITHMETIC_OPERATIONS_ASSIGNMENT(-, std::complex); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - scalar_operator& scalar_operator::operator op##=( \ - const scalar_operator &other) { \ - if (std::holds_alternative>(this->value) && \ - std::holds_alternative>(other.value)) { \ - this->value = \ - std::get>(this->value) op \ - std::get>(other.value); \ - return *this; \ - } \ - auto newGenerator = \ - [other, *this]( \ - const std::unordered_map> ¶meters) { \ - return this->evaluate(parameters) op##= other.evaluate(parameters); \ - }; \ - this->value = newGenerator; \ - return *this; \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + scalar_operator &scalar_operator::operator op##=( \ + const scalar_operator &other) { \ + if (std::holds_alternative>(this->value) && \ + std::holds_alternative>(other.value)) { \ + this->value = std::get>(this->value) \ + op std::get>(other.value); \ + return *this; \ + } \ + auto newGenerator = \ + [other, \ + *this](const std::unordered_map> \ + ¶meters) { \ + return this->evaluate(parameters) op## = other.evaluate(parameters); \ + }; \ + this->value = newGenerator; \ + return *this; \ } ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*); @@ -176,9 +186,9 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-); -#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ - scalar_operator operator op(scalar_operator &&self, otherTy other) { \ - return std::move(self op##= other); \ +#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ + scalar_operator operator op(scalar_operator &&self, otherTy other) { \ + return std::move(self op## = other); \ } ARITHMETIC_OPERATIONS_RVALUE(*, double); @@ -192,18 +202,17 @@ ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); // left-hand arithmetics -#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ - scalar_operator operator op(otherTy other, const scalar_operator &self) { \ - if (std::holds_alternative>(self.value)) { \ - return scalar_operator( \ - other op std::get>(self.value)); \ - } \ - auto newGenerator = \ - [other, generator = std::get(self.value)]( \ - const std::unordered_map> ¶meters) { \ - return other op generator(parameters); \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ + scalar_operator operator op(otherTy other, const scalar_operator &self) { \ + if (std::holds_alternative>(self.value)) { \ + return scalar_operator( \ + other op std::get>(self.value)); \ + } \ + auto newGenerator = \ + [other, generator = std::get(self.value)]( \ + const std::unordered_map> \ + ¶meters) { return other op generator(parameters); }; \ + return scalar_operator(newGenerator); \ } ARITHMETIC_OPERATIONS_REVERSE(*, double); diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 1e8f024106..6736085c78 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -18,18 +18,27 @@ namespace cudaq { // private helpers std::string spin_operator::op_code_to_string() const { - if (this->op_code == 1) return "Z"; - else if (this->op_code == 2) return "X"; - else if (this->op_code == 3) return "Y"; - else return "I"; + if (this->op_code == 1) + return "Z"; + else if (this->op_code == 2) + return "X"; + else if (this->op_code == 3) + return "Y"; + else + return "I"; } std::complex spin_operator::inplace_mult(const spin_operator &other) { assert(this->target == other.target); std::complex factor; - if (this->op_code == 0 || other.op_code == 0 || this->op_code == other.op_code) factor = 1.0; - else if (this->op_code + 1 == other.op_code || this->op_code - 2 == other.op_code) factor = 1.0j; - else factor = -1.0j; + if (this->op_code == 0 || other.op_code == 0 || + this->op_code == other.op_code) + factor = 1.0; + else if (this->op_code + 1 == other.op_code || + this->op_code - 2 == other.op_code) + factor = 1.0j; + else + factor = -1.0j; this->op_code ^= other.op_code; this->id = this->op_code_to_string() + std::to_string(target); return factor; @@ -37,29 +46,27 @@ std::complex spin_operator::inplace_mult(const spin_operator &other) { // read-only properties -const std::string& spin_operator::unique_id() const { - return this->id; -} +const std::string &spin_operator::unique_id() const { return this->id; } -std::vector spin_operator::degrees() const { - return {this->target}; -} +std::vector spin_operator::degrees() const { return {this->target}; } // constructors -spin_operator::spin_operator(int target) - : op_code(0), target(target), id("I" + std::to_string(target)) {} +spin_operator::spin_operator(int target) + : op_code(0), target(target), id("I" + std::to_string(target)) {} -spin_operator::spin_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); - this->id = this->op_code_to_string() + std::to_string(target); +spin_operator::spin_operator(int target, int op_id) + : op_code(op_id), target(target) { + assert(0 <= op_id < 4); + this->id = this->op_code_to_string() + std::to_string(target); } // evaluations -matrix_2 spin_operator::to_matrix(std::unordered_map &dimensions, - const std::unordered_map> ¶meters) const { +matrix_2 spin_operator::to_matrix( + std::unordered_map &dimensions, + const std::unordered_map> ¶meters) + const { auto it = dimensions.find(this->target); if (it == dimensions.end()) dimensions[this->target] = 2; @@ -84,8 +91,10 @@ matrix_2 spin_operator::to_matrix(std::unordered_map &dimensions, } std::string spin_operator::to_string(bool include_degrees) const { - if (include_degrees) return this->op_code_to_string() + "(" + std::to_string(target) + ")"; - else return this->op_code_to_string(); + if (include_degrees) + return this->op_code_to_string() + "(" + std::to_string(target) + ")"; + else + return this->op_code_to_string(); } // comparisons diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 7c2fea27c3..8b4970a8c7 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -12,20 +12,19 @@ #include #include -#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" namespace cudaq { -template +template class product_operator; // FIXME: rename to spin ... -class spin_operator : public operator_handler{ -friend class product_operator; +class spin_operator : public operator_handler { + friend class product_operator; private: - // I = 0, Z = 1, X = 2, Y = 3 int op_code; int target; @@ -40,10 +39,9 @@ friend class product_operator; std::complex inplace_mult(const spin_operator &other); public: - // read-only properties - virtual const std::string& unique_id() const; + virtual const std::string &unique_id() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. @@ -62,8 +60,10 @@ friend class product_operator; /// that is, the dimension of each degree of freedom /// that the operator acts on. Example for two, 2-level /// degrees of freedom: `{0 : 2, 1 : 2}`. - virtual matrix_2 to_matrix(std::unordered_map &dimensions, - const std::unordered_map> ¶meters = {}) const; + virtual matrix_2 + to_matrix(std::unordered_map &dimensions, + const std::unordered_map> + ¶meters = {}) const; virtual std::string to_string(bool include_degrees) const; diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 6ed00dac5f..41ad71de45 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -6,191 +6,236 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + #include #include -#include "operator_leafs.h" +#include "boson_operators.h" #include "matrix_operators.h" +#include "operator_leafs.h" #include "spin_operators.h" -#include "boson_operators.h" namespace cudaq { -template +template class product_operator; template class operator_sum; -#define TYPE_CONVERSION_CONSTRAINT(LHtype, RHtype) \ - std::enable_if_t::value && \ - !std::is_same::value && \ - std::is_base_of::value && \ - std::is_base_of::value, bool> - -template -product_operator operator*(double other, const product_operator &self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator-(double other, const product_operator &self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); - -template -product_operator operator*(const product_operator &other, const product_operator &self); -template -operator_sum operator+(const product_operator &other, const product_operator &self); -template -operator_sum operator-(const product_operator &other, const product_operator &self); - -template -operator_sum operator*(double other, const operator_sum &self); -template -operator_sum operator+(double other, const operator_sum &self); -template -operator_sum operator-(double other, const operator_sum &self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); - -template -operator_sum operator*(const operator_sum &other, const product_operator &self); -template -operator_sum operator+(const operator_sum &other, const product_operator &self); -template -operator_sum operator-(const operator_sum &other, const product_operator &self); -template -operator_sum operator*(const product_operator &other, const operator_sum &self); -template -operator_sum operator+(const product_operator &other, const operator_sum &self); -template -operator_sum operator-(const product_operator &other, const operator_sum &self); -template -operator_sum operator*(const operator_sum &other, const operator_sum &self); -template -operator_sum operator+(const operator_sum &other, const operator_sum &self); -template -operator_sum operator-(const operator_sum &other, const operator_sum &self); +#define TYPE_CONVERSION_CONSTRAINT(LHtype, RHtype) \ + std::enable_if_t::value && \ + !std::is_same::value && \ + std::is_base_of::value && \ + std::is_base_of::value, \ + bool> +template +product_operator operator*(double other, + const product_operator &self); +template +operator_sum operator+(double other, + const product_operator &self); +template +operator_sum operator-(double other, + const product_operator &self); +template +product_operator operator*(std::complex other, + const product_operator &self); +template +operator_sum operator+(std::complex other, + const product_operator &self); +template +operator_sum operator-(std::complex other, + const product_operator &self); +template +product_operator operator*(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, + const product_operator &self); + +template +product_operator +operator*(const product_operator &other, + const product_operator &self); +template +operator_sum operator+(const product_operator &other, + const product_operator &self); +template +operator_sum operator-(const product_operator &other, + const product_operator &self); + +template +operator_sum operator*(double other, + const operator_sum &self); +template +operator_sum operator+(double other, + const operator_sum &self); +template +operator_sum operator-(double other, + const operator_sum &self); +template +operator_sum operator*(std::complex other, + const operator_sum &self); +template +operator_sum operator+(std::complex other, + const operator_sum &self); +template +operator_sum operator-(std::complex other, + const operator_sum &self); +template +operator_sum operator*(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, + const operator_sum &self); + +template +operator_sum operator*(const operator_sum &other, + const product_operator &self); +template +operator_sum operator+(const operator_sum &other, + const product_operator &self); +template +operator_sum operator-(const operator_sum &other, + const product_operator &self); +template +operator_sum operator*(const product_operator &other, + const operator_sum &self); +template +operator_sum operator+(const product_operator &other, + const operator_sum &self); +template +operator_sum operator-(const product_operator &other, + const operator_sum &self); +template +operator_sum operator*(const operator_sum &other, + const operator_sum &self); +template +operator_sum operator+(const operator_sum &other, + const operator_sum &self); +template +operator_sum operator-(const operator_sum &other, + const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES -#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ - \ - extern template \ - product_operator operator*(double other, const product_operator &self); \ - extern template \ - operator_sum operator+(double other, const product_operator &self); \ - extern template \ - operator_sum operator-(double other, const product_operator &self); \ - extern template \ - product_operator operator*(std::complex other, const product_operator &self); \ - extern template \ - operator_sum operator+(std::complex other, const product_operator &self); \ - extern template \ - operator_sum operator-(std::complex other, const product_operator &self); \ - extern template \ - product_operator operator*(const scalar_operator &other, const product_operator &self); \ - extern template \ - operator_sum operator+(const scalar_operator &other, const product_operator &self); \ - extern template \ - operator_sum operator-(const scalar_operator &other, const product_operator &self); \ - \ - extern template \ - operator_sum operator*(double other, const operator_sum &self); \ - extern template \ - operator_sum operator+(double other, const operator_sum &self); \ - extern template \ - operator_sum operator-(double other, const operator_sum &self); \ - extern template \ - operator_sum operator*(std::complex other, const operator_sum &self); \ - extern template \ - operator_sum operator+(std::complex other, const operator_sum &self); \ - extern template \ - operator_sum operator-(std::complex other, const operator_sum &self); \ - extern template \ - operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ - extern template \ - operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ - extern template \ - operator_sum operator-(const scalar_operator &other, const operator_sum &self); +#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ + \ + extern template product_operator operator*( \ + double other, const product_operator &self); \ + extern template operator_sum operator+( \ + double other, const product_operator &self); \ + extern template operator_sum operator-( \ + double other, const product_operator &self); \ + extern template product_operator operator*( \ + std::complex other, const product_operator &self); \ + extern template operator_sum operator+( \ + std::complex other, const product_operator &self); \ + extern template operator_sum operator-( \ + std::complex other, const product_operator &self); \ + extern template product_operator operator*( \ + const scalar_operator &other, const product_operator &self); \ + extern template operator_sum operator+( \ + const scalar_operator &other, const product_operator &self); \ + extern template operator_sum operator-( \ + const scalar_operator &other, const product_operator &self); \ + \ + extern template operator_sum operator*( \ + double other, const operator_sum &self); \ + extern template operator_sum operator+( \ + double other, const operator_sum &self); \ + extern template operator_sum operator-( \ + double other, const operator_sum &self); \ + extern template operator_sum operator*( \ + std::complex other, const operator_sum &self); \ + extern template operator_sum operator+( \ + std::complex other, const operator_sum &self); \ + extern template operator_sum operator-( \ + std::complex other, const operator_sum &self); \ + extern template operator_sum operator*( \ + const scalar_operator &other, const operator_sum &self); \ + extern template operator_sum operator+( \ + const scalar_operator &other, const operator_sum &self); \ + extern template operator_sum operator-( \ + const scalar_operator &other, const operator_sum &self); EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(boson_operator); -#define EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(op, returnTy) \ - \ - extern template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - extern template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - extern template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - extern template \ - returnTy operator op(const product_operator &other, \ - const product_operator &self); \ - \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const product_operator &self); \ - \ - extern template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - extern template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - extern template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - extern template \ - operator_sum operator op(const product_operator &other, \ - const operator_sum &self); \ - \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ - extern template \ - operator_sum operator op(const operator_sum &other, \ - const operator_sum &self); \ +#define EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(op, returnTy) \ + \ + extern template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + extern template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + extern template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + extern template returnTy operator op( \ + const product_operator &other, \ + const product_operator &self); \ + \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const product_operator &self); \ + \ + extern template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + extern template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + extern template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + extern template operator_sum operator op( \ + const product_operator &other, \ + const operator_sum &self); \ + \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); \ + extern template operator_sum operator op( \ + const operator_sum &other, \ + const operator_sum &self); EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(*, product_operator); EXTERN_CONVERSION_TEMPLATE_SPECIALIZATIONS(+, operator_sum); diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index f071c4726d..fcc3ad7b4b 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -24,8 +24,7 @@ namespace cudaq { evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, - const state &initial_state, - BaseIntegrator& integrator, + const state &initial_state, BaseIntegrator &integrator, const std::vector *> &collapse_operators = {}, const std::vector *> &observables = {}, diff --git a/runtime/cudaq/helpers.h b/runtime/cudaq/helpers.h deleted file mode 100644 index 51c8e4bff4..0000000000 --- a/runtime/cudaq/helpers.h +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudaq/utils/tensor.h" -#include -#include - -namespace cudaq { -namespace detail { - -// Aggregate parameters from multiple mappings. -std::map aggregate_parameters( - const std::vector> ¶meter_mappings); - -// Extract documentation for a specific parameter from docstring. -std::string parameter_docs(const std::string ¶m_name, - const std::string &docs); - -// Extract positional arguments and keyword-only arguments. -std::pair, std::map> -args_from_kwargs(const std::map &kwargs, - const std::vector &required_args, - const std::vector &kwonly_args); - -/// Generates all possible states for the given dimensions ordered according -/// to the sequence of degrees (ordering is relevant if dimensions differ). -std::vector generate_all_states(std::vector degrees, - std::map dimensions); - -// Permutes the given matrix according to the given permutation. -// If states is the current order of vector entries on which the given matrix -// acts, and permuted_states is the desired order of an array on which the -// permuted matrix should act, then the permutation is defined such that -// [states[i] for i in permutation] produces permuted_states. -cudaq::matrix_2 permute_matrix(cudaq::matrix_2 matrix, - std::vector permutation); - -// Returns the degrees sorted in canonical order. -std::vector canonicalize_degrees(std::vector degrees); -} // namespace detail -} // namespace cudaq diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 292cd88276..c64e50a20f 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,19 +8,18 @@ #pragma once -#include -#include - -#include "utils/tensor.h" #include "dynamics/operator_leafs.h" #include "dynamics/templates.h" +#include "utils/tensor.h" +#include +#include namespace cudaq { class MatrixArithmetics; class EvaluatedMatrix; -// fixme: write overloads with rvalue qualifiers +// fixme: write overloads with rvalue qualifiers // https://stackoverflow.com/questions/37737798/c-is-it-possible-to-overload-the-unary-minus-operator-of-an-rvalue-reference /// @brief Represents an operator expression consisting of a sum of terms, where @@ -29,11 +28,12 @@ class EvaluatedMatrix; /// to convert them to data types that can. template class operator_sum { -template friend class operator_sum; -template friend class product_operator; + template + friend class operator_sum; + template + friend class product_operator; private: - // inserts a new term combining it with an existing one if possible void insert(product_operator &&other); void insert(const product_operator &other); @@ -42,17 +42,20 @@ template friend class product_operator; void aggregate_terms(); - template - void aggregate_terms(product_operator &&head, Args&& ... args); + template + void aggregate_terms(product_operator &&head, Args &&...args); - EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms = true) const; protected: - std::unordered_map> tmap; - template, Args>...>::value, bool> = true> - operator_sum(Args&&... args); + template , Args>...>::value, + bool> = true> + operator_sum(Args &&...args); operator_sum(std::vector> &&terms); @@ -73,7 +76,10 @@ template friend class product_operator; operator_sum(const product_operator &other); - template::value && std::is_constructible::value, bool> = true> + template ::value && + std::is_constructible::value, + bool> = true> operator_sum(const operator_sum &other); // copy constructor @@ -86,15 +92,21 @@ template friend class product_operator; // assignments - template::value && std::is_constructible::value, bool> = true> - operator_sum& operator=(const product_operator &other); + template ::value && + std::is_constructible::value, + bool> = true> + operator_sum &operator=(const product_operator &other); - operator_sum& operator=(const product_operator &other); + operator_sum &operator=(const product_operator &other); - operator_sum& operator=(product_operator &&other); + operator_sum &operator=(product_operator &&other); - template::value && std::is_constructible::value, bool> = true> - operator_sum& operator=(const operator_sum &other); + template ::value && + std::is_constructible::value, + bool> = true> + operator_sum &operator=(const operator_sum &other); // assignment operator operator_sum &operator=(const operator_sum &other); @@ -115,7 +127,8 @@ template friend class product_operator; /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. matrix_2 to_matrix(std::unordered_map dimensions = {}, - const std::unordered_map> ¶meters = {}) const; + const std::unordered_map> + ¶meters = {}) const; // unary operators @@ -134,28 +147,31 @@ template friend class product_operator; operator_sum operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; + operator_sum + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const; + operator_sum + operator-(const product_operator &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; operator_sum operator*(const operator_sum &other) const; - operator_sum& operator*=(double other); - operator_sum& operator+=(double other); - operator_sum& operator-=(double other); - operator_sum& operator*=(std::complex other); - operator_sum& operator+=(std::complex other); - operator_sum& operator-=(std::complex other); - operator_sum& operator*=(const scalar_operator &other); - operator_sum& operator+=(const scalar_operator &other); - operator_sum& operator-=(const scalar_operator &other); - operator_sum& operator*=(const product_operator &other); - operator_sum& operator+=(const product_operator &other); - operator_sum& operator-=(const product_operator &other); - operator_sum& operator*=(const operator_sum &other); - operator_sum& operator+=(const operator_sum &other); - operator_sum& operator-=(const operator_sum &other); + operator_sum &operator*=(double other); + operator_sum &operator+=(double other); + operator_sum &operator-=(double other); + operator_sum &operator*=(std::complex other); + operator_sum &operator+=(std::complex other); + operator_sum &operator-=(std::complex other); + operator_sum &operator*=(const scalar_operator &other); + operator_sum &operator+=(const scalar_operator &other); + operator_sum &operator-=(const scalar_operator &other); + operator_sum &operator*=(const product_operator &other); + operator_sum &operator+=(const product_operator &other); + operator_sum &operator-=(const product_operator &other); + operator_sum &operator*=(const operator_sum &other); + operator_sum &operator+=(const operator_sum &other); + operator_sum &operator-=(const operator_sum &other); // left-hand arithmetics @@ -167,35 +183,47 @@ template friend class product_operator; friend operator_sum operator+(double other, const operator_sum &self); template friend operator_sum operator-(double other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, const operator_sum &self); - template - friend operator_sum operator+(std::complex other, const operator_sum &self); - template - friend operator_sum operator-(std::complex other, const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - - template - friend operator_sum operator+(double other, const product_operator &self); - template - friend operator_sum operator-(double other, const product_operator &self); - template - friend operator_sum operator+(std::complex other, const product_operator &self); - template - friend operator_sum operator-(std::complex other, const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator*(std::complex other, + const operator_sum &self); + template + friend operator_sum operator+(std::complex other, + const operator_sum &self); + template + friend operator_sum operator-(std::complex other, + const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, + const operator_sum &self); + + template + friend operator_sum operator+(double other, + const product_operator &self); + template + friend operator_sum operator-(double other, + const product_operator &self); + template + friend operator_sum operator+(std::complex other, + const product_operator &self); + template + friend operator_sum operator-(std::complex other, + const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, + const product_operator &self); // common operators - template + template friend operator_sum operator_handler::empty(); }; @@ -205,28 +233,42 @@ template friend class product_operator; /// that can. template class product_operator { -template friend class product_operator; -template friend class operator_sum; + template + friend class product_operator; + template + friend class operator_sum; private: // template defined as long as T implements an in-place multiplication - // won't work if the in-place multiplication was inherited from a base class - template ::value, bool> = true> + template ::value, + bool> = true> static std::true_type handler_mult(int); - template - static std::false_type handler_mult(...); // ellipsis ensures the template above is picked if it exists - static constexpr bool supports_inplace_mult = std::is_same(0)), std::true_type>::value; + template + static std::false_type handler_mult( + ...); // ellipsis ensures the template above is picked if it exists + static constexpr bool supports_inplace_mult = + std::is_same(0)), std::true_type>::value; #if !defined(NDEBUG) bool is_canonicalized() const; #endif - - std::vector::const_iterator find_insert_at(const HandlerTy &other) const; - template::value && !product_operator::supports_inplace_mult, int> = 0> + std::vector::const_iterator + find_insert_at(const HandlerTy &other) const; + + template ::value && + !product_operator::supports_inplace_mult, + int> = 0> void insert(T &&other, bool update_id); - template ::value && product_operator::supports_inplace_mult, bool> = true> + template ::value && + product_operator::supports_inplace_mult, + bool> = true> void insert(T &&other, bool update_id); void update_id(); @@ -234,24 +276,33 @@ template friend class operator_sum; void aggregate_terms(); template - void aggregate_terms(HandlerTy &&head, Args&& ... args); + void aggregate_terms(HandlerTy &&head, Args &&...args); - EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, bool pad_terms = true) const; + EvaluatedMatrix m_evaluate(MatrixArithmetics arithmetics, + bool pad_terms = true) const; protected: - std::vector operators; scalar_operator coefficient; std::string term_id; - template...>::value, bool> = true> - product_operator(scalar_operator coefficient, Args&&... args); + template ...>::value, + bool> = true> + product_operator(scalar_operator coefficient, Args &&...args); - // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, const std::vector &atomic_operators, const std::string &term_id); + // keep this constructor protected (otherwise it needs to ensure canonical + // order) + product_operator(scalar_operator coefficient, + const std::vector &atomic_operators, + const std::string &term_id); - // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, std::vector &&atomic_operators, std::string &&term_id); + // keep this constructor protected (otherwise it needs to ensure canonical + // order) + product_operator(scalar_operator coefficient, + std::vector &&atomic_operators, + std::string &&term_id); public: // read-only properties @@ -265,7 +316,7 @@ template friend class operator_sum; int num_terms() const; /// FIXME: GET RID OF THIS (MAKE ITERABLE INSTEAD) - const std::vector& get_terms() const; + const std::vector &get_terms() const; scalar_operator get_coefficient() const; @@ -275,7 +326,10 @@ template friend class operator_sum; product_operator(HandlerTy &&atomic); - template::value && std::is_constructible::value, bool> = true> + template ::value && + std::is_constructible::value, + bool> = true> product_operator(const product_operator &other); // copy constructor @@ -288,8 +342,11 @@ template friend class operator_sum; // assignments - template::value && std::is_constructible::value, bool> = true> - product_operator& operator=(const product_operator &other); + template ::value && + std::is_constructible::value, + bool> = true> + product_operator &operator=(const product_operator &other); // assignment operator product_operator & @@ -311,7 +368,8 @@ template friend class operator_sum; /// @arg `parameters` : A map of the parameter names to their concrete, /// complex values. matrix_2 to_matrix(std::unordered_map dimensions = {}, - const std::unordered_map> ¶meters = {}) const; + const std::unordered_map> + ¶meters = {}) const; // comparisons @@ -344,61 +402,83 @@ template friend class operator_sum; product_operator operator*(const scalar_operator &other) const; operator_sum operator+(const scalar_operator &other) const; operator_sum operator-(const scalar_operator &other) const; - product_operator operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const; - operator_sum operator-(const product_operator &other) const; + product_operator + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const; + operator_sum + operator-(const product_operator &other) const; operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const; operator_sum operator-(const operator_sum &other) const; - product_operator& operator*=(double other); - product_operator& operator*=(std::complex other); - product_operator& operator*=(const scalar_operator &other); - product_operator& operator*=(const product_operator &other); + product_operator &operator*=(double other); + product_operator &operator*=(std::complex other); + product_operator &operator*=(const scalar_operator &other); + product_operator & + operator*=(const product_operator &other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. - template - friend product_operator operator*(double other, const product_operator &self); - template - friend operator_sum operator+(double other, const product_operator &self); - template - friend operator_sum operator-(double other, const product_operator &self); - template + // Being a bit permissive here, since otherwise the explicit template + // instantiation is a nightmare. + template + friend product_operator operator*(double other, + const product_operator &self); + template + friend operator_sum operator+(double other, + const product_operator &self); + template + friend operator_sum operator-(double other, + const product_operator &self); + template friend operator_sum operator*(double other, const operator_sum &self); - template + template friend operator_sum operator+(double other, const operator_sum &self); - template + template friend operator_sum operator-(double other, const operator_sum &self); - template - friend product_operator operator*(std::complex other, const product_operator &self); - template - friend operator_sum operator+(std::complex other, const product_operator &self); - template - friend operator_sum operator-(std::complex other, const product_operator &self); - template - friend operator_sum operator*(std::complex other, const operator_sum &self); - template - friend operator_sum operator+(std::complex other, const operator_sum &self); - template - friend operator_sum operator-(std::complex other, const operator_sum &self); - template - friend product_operator operator*(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + template + friend product_operator operator*(std::complex other, + const product_operator &self); + template + friend operator_sum operator+(std::complex other, + const product_operator &self); + template + friend operator_sum operator-(std::complex other, + const product_operator &self); + template + friend operator_sum operator*(std::complex other, + const operator_sum &self); + template + friend operator_sum operator+(std::complex other, + const operator_sum &self); + template + friend operator_sum operator-(std::complex other, + const operator_sum &self); + template + friend product_operator operator*(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator*(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, + const operator_sum &self); // common operators - template...>::value, bool>> + template ...>::value, bool>> friend product_operator operator_handler::identity(Args... targets); }; diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index ed52612f97..6ff6d663b9 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -31,7 +31,8 @@ class runge_kutta_integrator : public BaseIntegrator { void set_state(cudm_state &&initial_state); void set_stepper(std::shared_ptr stepper); std::pair get_state() override; - std::pair get_cudm_state(); + std::pair get_cudm_state(); + private: std::unique_ptr m_state; double m_t; diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index b1694b647b..04fc77be97 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -7,27 +7,25 @@ ******************************************************************************/ #include "cudaq/evolution.h" -#include "cudm_error_handling.h" #include "cudaq/runge_kutta_integrator.h" -#include +#include "cudm_error_handling.h" #include "cudm_expectation.h" +#include "cudm_helpers.h" +#include "cudm_state.h" #include "cudm_time_stepper.h" +#include #include #include #include -#include "cudm_helpers.h" -#include "cudm_state.h" namespace cudaq { evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, - const state &initial_state, - BaseIntegrator& in_integrator, + const state &initial_state, BaseIntegrator &in_integrator, const std::vector *> &collapse_operators, const std::vector *> &observables, - bool store_intermediate_results, - std::optional shots_count) { + bool store_intermediate_results, std::optional shots_count) { cudensitymatHandle_t handle; HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); @@ -81,7 +79,8 @@ evolve_result evolve_single( std::vector expVals; for (auto &expectation : expectations) { expectation.prepare(finalState->get_impl()); - const auto expVal = expectation.compute(finalState->get_impl(), finalTime); + const auto expVal = + expectation.compute(finalState->get_impl(), finalTime); expVals.emplace_back(expVal.real()); } // TODO: need to convert to proper state diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index e6a4ea9bb5..55e4c8a912 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -39,9 +39,7 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { } catch (const std::exception &) { } - ScalarCallbackFunction generator = scalar_op.get_generator(); - - if (!generator) { + if (scalar_op.is_constant()) { throw std::runtime_error( "scalar_operator does not have a valid generator function."); } @@ -53,7 +51,8 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { scalar_operator *scalar_op = static_cast(scalar_storage); - std::map> param_map; + std::unordered_map> param_map; + // FIXME: Figure how to populate the param map for (size_t i = 0; i < num_params; i++) { param_map[std::to_string(i)] = params[i]; } @@ -95,12 +94,13 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op) { try { matrix_operator *mat_op = static_cast(tensor_storage); - std::map> param_map; + std::unordered_map> param_map; for (size_t i = 0; i < num_params; i++) { param_map[std::to_string(i)] = params[i]; } - matrix_2 matrix_data = mat_op->to_matrix({}, param_map); + std::unordered_map dimensions = {}; + matrix_2 matrix_data = mat_op->to_matrix(dimensions, param_map); std::size_t rows = matrix_data.get_rows(); std::size_t cols = matrix_data.get_columns(); @@ -195,11 +195,11 @@ void cudm_helper::print_complex_vector( // Need to use std::variant cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( const cudaq::matrix_operator &elem_op, - const std::map> ¶meters, + const std::unordered_map> ¶meters, const std::vector &mode_extents) { - auto subspace_extents = get_subspace_extents(mode_extents, elem_op.degrees); - auto flat_matrix = flatten_matrix( - elem_op.to_matrix(convert_dimensions(mode_extents), parameters)); + auto subspace_extents = get_subspace_extents(mode_extents, elem_op.degrees()); + std::unordered_map dimensions = convert_dimensions(mode_extents); + auto flat_matrix = flatten_matrix(elem_op.to_matrix(dimensions, parameters)); if (flat_matrix.empty()) { throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); @@ -304,7 +304,7 @@ void cudm_helper::append_scalar_to_term(cudensitymatOperatorTerm_t term, const scalar_operator &scalar_op) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - if (!scalar_op.get_generator()) { + if (scalar_op.is_constant()) { std::complex coeff = scalar_op.evaluate({}); HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( handle, term, 0, nullptr, nullptr, nullptr, @@ -333,7 +333,7 @@ std::pair cudm_helper::compute_lindblad_operator_terms( operator_sum &collapseOp, const std::vector &mode_extents) { - std::map dimensions; + std::unordered_map dimensions; for (int i = 0; i < mode_extents.size(); ++i) dimensions[i] = mode_extents[i]; auto c_op = collapseOp.to_matrix(dimensions); @@ -548,10 +548,10 @@ cudensitymatOperator_t cudm_helper::compute_lindblad_operator( return liouvillian; } -std::map +std::unordered_map cudm_helper::convert_dimensions(const std::vector &mode_extents) { - std::map dimensions; + std::unordered_map dimensions; for (size_t i = 0; i < mode_extents.size(); i++) { dimensions[static_cast(i)] = static_cast(mode_extents[i]); } @@ -586,7 +586,7 @@ cudm_helper::convert_to_cudensitymat( auto cudm_elem_op = create_elementary_operator(*elem_op, {}, mode_extents); elem_ops.emplace_back(cudm_elem_op); - all_degrees.emplace_back(elem_op->degrees); + all_degrees.emplace_back(elem_op->degrees()); } else { // Catch anything that we don't know throw std::runtime_error("Unhandled type!"); @@ -600,7 +600,7 @@ cudm_helper::convert_to_cudensitymat( template cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( - const std::map> ¶meters, + const std::unordered_map> ¶meters, const operator_sum &op, const std::vector &mode_extents) { if (op.get_terms().empty()) { @@ -615,7 +615,7 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - if (!coeff.get_generator()) { + if (coeff.is_constant()) { const auto coeffVal = coeff.evaluate(); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle, operator_handle, term, 0, @@ -637,7 +637,7 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( const std::vector *> &collapse_operators, const std::vector &mode_extents, - const std::map> ¶meters, + const std::unordered_map> ¶meters, bool is_master_equation) { if (!is_master_equation && collapse_operators.empty()) { cudaq::info("Construct state vector Liouvillian"); @@ -654,7 +654,7 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( // Handle the Hamiltonian for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - if (!coeff.get_generator()) { + if (coeff.is_constant()) { const auto coeffVal = coeff.evaluate(); const auto leftCoeff = std::complex(0.0, -1.0) * coeffVal; // -i constant (left multiplication) @@ -765,7 +765,7 @@ void cudm_helper::destroy_array_gpu(void *gpu_array) { template cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( - const std::map> &, + const std::unordered_map> &, const operator_sum &, const std::vector &); } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index 8e4a2e3ee5..9342989d1e 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -14,8 +14,8 @@ #include #include #include -#include #include +#include namespace cudaq { class cudm_helper { public: @@ -41,7 +41,7 @@ class cudm_helper { // Convert operator sum to cudensitymat operator template cudensitymatOperator_t convert_to_cudensitymat_operator( - const std::map> ¶meters, + const std::unordered_map> ¶meters, const operator_sum &op, const std::vector &mode_extents); @@ -54,7 +54,7 @@ class cudm_helper { const std::vector *> &collapse_operators, const std::vector &mode_extents, - const std::map> ¶meters, + const std::unordered_map> ¶meters, bool is_master_equation); // Construct Liouvillian @@ -69,7 +69,7 @@ class cudm_helper { const std::vector &mode_extents); // Helper Functions - std::map + std::unordered_map convert_dimensions(const std::vector &mode_extents); std::vector get_subspace_extents(const std::vector &mode_extents, @@ -86,7 +86,7 @@ class cudm_helper { const scalar_operator &scalar_op); cudensitymatElementaryOperator_t create_elementary_operator( const cudaq::matrix_operator &elem_op, - const std::map> ¶meters, + const std::unordered_map> ¶meters, const std::vector &mode_extents); void append_elementary_operator_to_term( cudensitymatOperatorTerm_t term, @@ -103,13 +103,13 @@ class cudm_helper { cudensitymatHandle_t handle; // Things that we create that need to be cleaned up. // Use a set so that it's safe to push pointer multiple times. - std::unordered_set m_deviceBuffers; + std::unordered_set m_deviceBuffers; std::unordered_set m_elementaryOperators; std::unordered_set m_operatorTerms; }; extern template cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( - const std::map> &, + const std::unordered_map> &, const operator_sum &, const std::vector &); } // namespace cudaq \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/cudm_op_conversion.h b/runtime/nvqir/cudensitymat/cudm_op_conversion.h index c13de93706..bce97a9bb9 100644 --- a/runtime/nvqir/cudensitymat/cudm_op_conversion.h +++ b/runtime/nvqir/cudensitymat/cudm_op_conversion.h @@ -8,9 +8,9 @@ #pragma once -#include "cudm_helpers.h" #include "cudaq/operators.h" #include "cudaq/schedule.h" +#include "cudm_helpers.h" #include #include diff --git a/runtime/nvqir/cudensitymat/cudm_solver.h b/runtime/nvqir/cudensitymat/cudm_solver.h index 80b01b669a..17bc1fd9ae 100644 --- a/runtime/nvqir/cudensitymat/cudm_solver.h +++ b/runtime/nvqir/cudensitymat/cudm_solver.h @@ -8,11 +8,11 @@ #pragma once +#include "cudm_helpers.h> +#include "cudm_state.h> #include "runtime/common/EvolveResult.h" #include #include -#include "cudm_helpers.h> -#include "cudm_state.h> #include #include #include diff --git a/runtime/nvqir/cudensitymat/cudm_state.cpp b/runtime/nvqir/cudensitymat/cudm_state.cpp index 84ed55e243..cd17daa90c 100644 --- a/runtime/nvqir/cudensitymat/cudm_state.cpp +++ b/runtime/nvqir/cudensitymat/cudm_state.cpp @@ -6,15 +6,15 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include #include "cudm_state.h" #include "cudm_error_handling.h" +#include +#include #include #include #include #include #include -#include #include namespace cudaq { diff --git a/runtime/nvqir/cudensitymat/cudm_state.h b/runtime/nvqir/cudensitymat/cudm_state.h index 36cf9a5900..803064df24 100644 --- a/runtime/nvqir/cudensitymat/cudm_state.h +++ b/runtime/nvqir/cudensitymat/cudm_state.h @@ -8,13 +8,13 @@ #pragma once -#include #include "cudm_error_handling.h" #include "cudm_helpers.h" +#include #include #include -#include #include +#include namespace cudaq { // Enum to specify the initial quantum state. diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index 1ff33ec2c8..b3697b9155 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -14,7 +14,7 @@ void runge_kutta_integrator::set_state(cudaq::state initial_state, double t0) { // TODO } std::pair runge_kutta_integrator::get_state() { - // TODO: + // TODO: return std::make_pair(0.0, cudaq::state(nullptr)); } @@ -102,7 +102,8 @@ runge_kutta_integrator::runge_kutta_integrator( m_state = std::make_unique(std::move(initial_state)); } -void runge_kutta_integrator::set_stepper(std::shared_ptr stepper) { +void runge_kutta_integrator::set_stepper( + std::shared_ptr stepper) { m_stepper = stepper; } diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 294968aead..0970f6f276 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -292,7 +292,6 @@ if (CUDA_FOUND) set(CUDAQ_DYNAMICS_TEST_SOURCES dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp - dynamics/rydberg_hamiltonian.cpp dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp @@ -306,7 +305,7 @@ if (CUDA_FOUND) target_link_libraries(test_dynamics PRIVATE cudaq-spin - cudaq-operators + cudaq-operator cudaq nvqir-dynamics ${CUDENSITYMAT_ROOT}/lib/libcudensitymat.so.0 diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index b2de44ceff..44ef8efb6d 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -6,13 +6,14 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "utils.h" #include "cudaq/operators.h" +#include "utils.h" #include TEST(OperatorExpressions, checkMatrixOpsUnary) { auto create = cudaq::matrix_operator::create(0); - utils::checkEqual((-create).to_matrix({{0,2}}), -1.0 * utils::create_matrix(2)); + utils::checkEqual((-create).to_matrix({{0, 2}}), + -1.0 * utils::create_matrix(2)); } TEST(OperatorExpressions, checkPreBuiltMatrixOps) { @@ -117,70 +118,86 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { } TEST(OperatorExpressions, checkCustomMatrixOps) { - auto level_count = 2; - std::unordered_map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; - - { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + auto level_count = 2; + std::unordered_map dimensions = { + {0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; - // op 0: - // momentum level+1 on 0 - // position level+2 on 1 - // op 1: - // number level on 3 - // create level+2 on 1 - auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); - auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 3}); - - auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), - utils::position_matrix(level_count + 2)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), - utils::number_matrix(level_count)); - - std::vector product_matrices = { - utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * utils::create_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1) - }; - std::vector product_reverse_matrices = { + { + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + // op 0: + // momentum level+1 on 0 + // position level+2 on 1 + // op 1: + // number level on 3 + // create level+2 on 1 + auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 3}); + + auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), + utils::position_matrix(level_count + 2)); + auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), + utils::number_matrix(level_count)); + + std::vector product_matrices = { utils::number_matrix(level_count), - utils::create_matrix(level_count + 2) * utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1) - }; - std::vector sum_matrices_term0 = { - utils::id_matrix(level_count), - utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1) - }; - std::vector sum_matrices_term1 = { + utils::position_matrix(level_count + 2) * + utils::create_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; + std::vector product_reverse_matrices = { utils::number_matrix(level_count), - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1) - }; - - auto expected_product = cudaq::kronecker(product_matrices.begin(), product_matrices.end()); - auto expected_product_reverse = cudaq::kronecker(product_reverse_matrices.begin(), product_reverse_matrices.end()); - auto expected_sum_term0 = cudaq::kronecker(sum_matrices_term0.begin(), sum_matrices_term0.end()); - auto expected_sum_term1 = cudaq::kronecker(sum_matrices_term1.begin(), sum_matrices_term1.end()); - - utils::checkEqual(op0.to_matrix(dimensions), matrix0); // *not* in canonical order; order as defined in custom op definition - utils::checkEqual(op1.to_matrix(dimensions), matrix1); // *not* in canonical order; order as defined in custom op definition - utils::checkEqual((op0 * op1).to_matrix(dimensions), expected_product); // now reordered in canonical order - utils::checkEqual((op1 * op0).to_matrix(dimensions), expected_product_reverse); // now reordered in canonical order - utils::checkEqual((op0 + op1).to_matrix(dimensions), expected_sum_term0 + expected_sum_term1); // now reordered in canonical order - utils::checkEqual((op1 + op0).to_matrix(dimensions), expected_sum_term0 + expected_sum_term1); // now reordered in canonical order + utils::create_matrix(level_count + 2) * + utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; + std::vector sum_matrices_term0 = { + utils::id_matrix(level_count), utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; + std::vector sum_matrices_term1 = { + utils::number_matrix(level_count), utils::create_matrix(level_count + 2), + utils::id_matrix(level_count + 1)}; + + auto expected_product = + cudaq::kronecker(product_matrices.begin(), product_matrices.end()); + auto expected_product_reverse = cudaq::kronecker( + product_reverse_matrices.begin(), product_reverse_matrices.end()); + auto expected_sum_term0 = + cudaq::kronecker(sum_matrices_term0.begin(), sum_matrices_term0.end()); + auto expected_sum_term1 = + cudaq::kronecker(sum_matrices_term1.begin(), sum_matrices_term1.end()); + + utils::checkEqual(op0.to_matrix(dimensions), + matrix0); // *not* in canonical order; order as defined in + // custom op definition + utils::checkEqual(op1.to_matrix(dimensions), + matrix1); // *not* in canonical order; order as defined in + // custom op definition + utils::checkEqual((op0 * op1).to_matrix(dimensions), + expected_product); // now reordered in canonical order + utils::checkEqual( + (op1 * op0).to_matrix(dimensions), + expected_product_reverse); // now reordered in canonical order + utils::checkEqual((op0 + op1).to_matrix(dimensions), + expected_sum_term0 + + expected_sum_term1); // now reordered in canonical order + utils::checkEqual((op1 + op0).to_matrix(dimensions), + expected_sum_term0 + + expected_sum_term1); // now reordered in canonical order } TEST(OperatorExpressions, checkMatrixOpsWithComplex) { @@ -194,7 +211,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { auto sum = value + elementary; auto reverse = elementary + value; - auto got_matrix = sum.to_matrix({{0,3}}); + auto got_matrix = sum.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils::id_matrix(3); @@ -205,14 +222,15 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `matrix_operator` - `complex` and `complex` - `matrix_operator` + // `matrix_operator` - `complex` and `complex` - + // `matrix_operator` { auto elementary = cudaq::matrix_operator::position(0); auto difference = value - elementary; auto reverse = elementary - value; - auto got_matrix = difference.to_matrix({{0,3}}); + auto got_matrix = difference.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils::id_matrix(3); @@ -231,7 +249,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { auto product = value * elementary; auto reverse = elementary * value; - auto got_matrix = product.to_matrix({{0,3}}); + auto got_matrix = product.to_matrix({{0, 3}}); auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils::id_matrix(3); @@ -245,7 +263,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { TEST(OperatorExpressions, checkMatrixOpsWithScalars) { - auto function = [](const std::unordered_map> ¶meters) { + auto function = [](const std::unordered_map> + ¶meters) { auto entry = parameters.find("value"); if (entry == parameters.end()) throw std::runtime_error("value not defined in parameters"); @@ -271,8 +290,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); - auto want_matrix = - utils::annihilate_matrix(level_count) + scaled_identity; + auto want_matrix = utils::annihilate_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils::annihilate_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); @@ -291,8 +309,10 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils::parity_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils::parity_matrix(level_count); @@ -315,7 +335,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto got_matrix = sum.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); auto want_matrix = utils::number_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils::number_matrix(level_count); + auto want_reverse_matrix = + scaled_identity - utils::number_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -332,10 +353,13 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); - auto got_matrix = sum.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = sum.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils::position_matrix(level_count) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils::position_matrix(level_count); + auto want_reverse_matrix = + scaled_identity - utils::position_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -379,8 +403,10 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { ASSERT_TRUE(reverse.degrees() == want_degrees); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils::create_matrix(level_count) * scaled_identity; auto want_reverse_matrix = scaled_identity * utils::create_matrix(level_count); @@ -393,7 +419,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { /// Keeping this fixed throughout. int level_count = 3; - std::unordered_map dimensions = {{0, level_count}, {1, level_count}}; + std::unordered_map dimensions = {{0, level_count}, + {1, level_count}}; // Addition, same DOF. { @@ -417,9 +444,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto sum = self + other; ASSERT_TRUE(sum.num_terms() == 2); - auto annihilate_full = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + auto annihilate_full = cudaq::kronecker( + utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils::create_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); @@ -449,9 +475,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto sum = self - other; ASSERT_TRUE(sum.num_terms() == 2); - auto annihilate_full = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + auto annihilate_full = cudaq::kronecker( + utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils::create_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = sum.to_matrix(dimensions); @@ -487,9 +512,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto annihilate_full = - cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + auto annihilate_full = cudaq::kronecker( + utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); auto create_full = cudaq::kronecker(utils::create_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = product.to_matrix(dimensions); @@ -507,8 +531,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `matrix_operator + operator_sum` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -524,7 +548,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::id_matrix(level_count)); auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full + term_0_full + term_1_full; auto want_reverse_matrix = term_0_full + term_1_full + self_full; utils::checkEqual(want_matrix, got_matrix); @@ -534,8 +559,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `matrix_operator - operator_sum` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -550,8 +575,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; utils::checkEqual(want_matrix, got_matrix); @@ -583,8 +609,10 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); - auto got_reverse_matrix = reverse.to_matrix({{0, level_count}, {1, level_count}}, {{"squeezing", value}}); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}, + {{"squeezing", value}}); + auto got_reverse_matrix = reverse.to_matrix( + {{0, level_count}, {1, level_count}}, {{"squeezing", value}}); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; utils::checkEqual(want_matrix, got_matrix); @@ -593,8 +621,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `operator_sum += matrix_operator` { - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum += cudaq::matrix_operator::displace(0); ASSERT_TRUE(operator_sum.num_terms() == 3); @@ -607,15 +635,16 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}, {{"displacement", value}}); + auto got_matrix = operator_sum.to_matrix( + {{0, level_count}, {1, level_count}}, {{"displacement", value}}); auto want_matrix = term_0_full + term_1_full + self_full; utils::checkEqual(want_matrix, got_matrix); } // `operator_sum -= matrix_operator` { - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum -= cudaq::matrix_operator::annihilate(0); ASSERT_TRUE(operator_sum.num_terms() == 3); @@ -627,7 +656,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = + operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = term_0_full + term_1_full - self_full; utils::checkEqual(want_matrix, got_matrix); } @@ -635,8 +665,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `operator_sum *= matrix_operator` { auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = cudaq::matrix_operator::create(0) + - cudaq::matrix_operator::identity(1); + auto operator_sum = + cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); operator_sum *= self; @@ -652,7 +682,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { utils::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = operator_sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix = + operator_sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = sum_full * self_full; utils::checkEqual(want_matrix, got_matrix); } @@ -664,16 +695,20 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } @@ -690,24 +725,31 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { ASSERT_THROW(custom_op0.to_matrix(), std::runtime_error); ASSERT_THROW(custom_op1.to_matrix({{1, 2}}), std::runtime_error); - ASSERT_THROW((custom_op1 * custom_op0).to_matrix({{0, 2}, {1, 2}}), std::runtime_error); - ASSERT_THROW((custom_op1 + custom_op0).to_matrix({{0, 2}, {1, 2}, {2, 2}}), std::runtime_error); + ASSERT_THROW((custom_op1 * custom_op0).to_matrix({{0, 2}, {1, 2}}), + std::runtime_error); + ASSERT_THROW((custom_op1 + custom_op0).to_matrix({{0, 2}, {1, 2}, {2, 2}}), + std::runtime_error); ASSERT_NO_THROW((custom_op0 * custom_op1).to_matrix(dimensions)); ASSERT_NO_THROW((custom_op0 + custom_op1).to_matrix(dimensions)); } TEST(OperatorExpressions, checkMatrixOpsParameterVerification) { - std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map> parameters = { + {"squeezing", 0.5}, {"displacement", 0.25}}; std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto squeeze = cudaq::matrix_operator::squeeze(1); auto displace = cudaq::matrix_operator::displace(0); ASSERT_THROW(squeeze.to_matrix(dimensions), std::runtime_error); - ASSERT_THROW(squeeze.to_matrix(dimensions, {{"displacement", 0.25}}), std::runtime_error); - ASSERT_THROW((squeeze * displace).to_matrix(dimensions, {{"displacement", 0.25}}), std::runtime_error); - ASSERT_THROW((squeeze + displace).to_matrix(dimensions, {{"squeezing", 0.5}}), std::runtime_error); + ASSERT_THROW(squeeze.to_matrix(dimensions, {{"displacement", 0.25}}), + std::runtime_error); + ASSERT_THROW( + (squeeze * displace).to_matrix(dimensions, {{"displacement", 0.25}}), + std::runtime_error); + ASSERT_THROW((squeeze + displace).to_matrix(dimensions, {{"squeezing", 0.5}}), + std::runtime_error); ASSERT_NO_THROW((squeeze * displace).to_matrix(dimensions, parameters)); ASSERT_NO_THROW((squeeze + displace).to_matrix(dimensions, parameters)); } diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index 8a2714c096..ee3ff7ceaa 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -6,13 +6,14 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "utils.h" #include "cudaq/operators.h" +#include "utils.h" #include TEST(OperatorExpressions, checkElementaryOpsConversions) { - std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map> parameters = { + {"squeezing", 0.5}, {"displacement", 0.25}}; std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto matrix_elementary = cudaq::matrix_operator::parity(1); @@ -22,331 +23,502 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { auto boson_elementary = cudaq::boson_operator::annihilate(1); auto boson_elementary_expected = utils::annihilate_matrix(2); - auto checkSumEquals = [dimensions, parameters]( - cudaq::operator_sum sum, - cudaq::matrix_2 expected, int expected_num_terms = 2) { - auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; - - auto checkProductEquals = [dimensions, parameters]( - cudaq::product_operator prod, - cudaq::matrix_2 expected, bool aggregated_prod = false) { - auto expected_num_terms = 2; - if (aggregated_prod) expected_num_terms = prod.degrees().size(); - auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; + auto checkSumEquals = + [dimensions, parameters](cudaq::operator_sum sum, + cudaq::matrix_2 expected, + int expected_num_terms = 2) { + auto got = sum.to_matrix(dimensions, parameters); + ASSERT_TRUE(sum.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; + + auto checkProductEquals = + [dimensions, + parameters](cudaq::product_operator prod, + cudaq::matrix_2 expected, bool aggregated_prod = false) { + auto expected_num_terms = 2; + if (aggregated_prod) + expected_num_terms = prod.degrees().size(); + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; // `elementary + elementary` { - checkSumEquals(matrix_elementary + matrix_elementary, matrix_elementary_expected + matrix_elementary_expected, 1); - checkSumEquals(spin_elementary + spin_elementary, spin_elementary_expected + spin_elementary_expected, 1); - checkSumEquals(boson_elementary + boson_elementary, boson_elementary_expected + boson_elementary_expected, 1); - checkSumEquals(matrix_elementary + spin_elementary, matrix_elementary_expected + spin_elementary_expected); - checkSumEquals(spin_elementary + matrix_elementary, matrix_elementary_expected + spin_elementary_expected); - checkSumEquals(matrix_elementary + boson_elementary, matrix_elementary_expected + boson_elementary_expected); - checkSumEquals(boson_elementary + matrix_elementary, matrix_elementary_expected + boson_elementary_expected); - checkSumEquals(spin_elementary + boson_elementary, spin_elementary_expected + boson_elementary_expected); - checkSumEquals(boson_elementary + spin_elementary, spin_elementary_expected + boson_elementary_expected); + checkSumEquals(matrix_elementary + matrix_elementary, + matrix_elementary_expected + matrix_elementary_expected, 1); + checkSumEquals(spin_elementary + spin_elementary, + spin_elementary_expected + spin_elementary_expected, 1); + checkSumEquals(boson_elementary + boson_elementary, + boson_elementary_expected + boson_elementary_expected, 1); + checkSumEquals(matrix_elementary + spin_elementary, + matrix_elementary_expected + spin_elementary_expected); + checkSumEquals(spin_elementary + matrix_elementary, + matrix_elementary_expected + spin_elementary_expected); + checkSumEquals(matrix_elementary + boson_elementary, + matrix_elementary_expected + boson_elementary_expected); + checkSumEquals(boson_elementary + matrix_elementary, + matrix_elementary_expected + boson_elementary_expected); + checkSumEquals(spin_elementary + boson_elementary, + spin_elementary_expected + boson_elementary_expected); + checkSumEquals(boson_elementary + spin_elementary, + spin_elementary_expected + boson_elementary_expected); } // `elementary - elementary` { - checkSumEquals(matrix_elementary - matrix_elementary, matrix_elementary_expected - matrix_elementary_expected, 1); - checkSumEquals(spin_elementary - spin_elementary, spin_elementary_expected - spin_elementary_expected, 1); - checkSumEquals(boson_elementary - boson_elementary, boson_elementary_expected - boson_elementary_expected, 1); - checkSumEquals(matrix_elementary - spin_elementary, matrix_elementary_expected - spin_elementary_expected); - checkSumEquals(spin_elementary - matrix_elementary, spin_elementary_expected - matrix_elementary_expected); - checkSumEquals(matrix_elementary - boson_elementary, matrix_elementary_expected - boson_elementary_expected); - checkSumEquals(boson_elementary - matrix_elementary, boson_elementary_expected - matrix_elementary_expected); - checkSumEquals(spin_elementary - boson_elementary, spin_elementary_expected - boson_elementary_expected); - checkSumEquals(boson_elementary - spin_elementary, boson_elementary_expected - spin_elementary_expected); + checkSumEquals(matrix_elementary - matrix_elementary, + matrix_elementary_expected - matrix_elementary_expected, 1); + checkSumEquals(spin_elementary - spin_elementary, + spin_elementary_expected - spin_elementary_expected, 1); + checkSumEquals(boson_elementary - boson_elementary, + boson_elementary_expected - boson_elementary_expected, 1); + checkSumEquals(matrix_elementary - spin_elementary, + matrix_elementary_expected - spin_elementary_expected); + checkSumEquals(spin_elementary - matrix_elementary, + spin_elementary_expected - matrix_elementary_expected); + checkSumEquals(matrix_elementary - boson_elementary, + matrix_elementary_expected - boson_elementary_expected); + checkSumEquals(boson_elementary - matrix_elementary, + boson_elementary_expected - matrix_elementary_expected); + checkSumEquals(spin_elementary - boson_elementary, + spin_elementary_expected - boson_elementary_expected); + checkSumEquals(boson_elementary - spin_elementary, + boson_elementary_expected - spin_elementary_expected); } // `elementary * elementary` { - checkProductEquals(matrix_elementary * matrix_elementary, matrix_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * spin_elementary, spin_elementary_expected * spin_elementary_expected, true); - checkProductEquals(boson_elementary * boson_elementary, boson_elementary_expected * boson_elementary_expected); - checkProductEquals(matrix_elementary * spin_elementary, matrix_elementary_expected * spin_elementary_expected); - checkProductEquals(spin_elementary * matrix_elementary, spin_elementary_expected * matrix_elementary_expected); - checkProductEquals(matrix_elementary * boson_elementary, matrix_elementary_expected * boson_elementary_expected); - checkProductEquals(boson_elementary * matrix_elementary, boson_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * boson_elementary, spin_elementary_expected * boson_elementary_expected); - checkProductEquals(boson_elementary * spin_elementary, boson_elementary_expected * spin_elementary_expected); + checkProductEquals(matrix_elementary * matrix_elementary, + matrix_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * spin_elementary, + spin_elementary_expected * spin_elementary_expected, + true); + checkProductEquals(boson_elementary * boson_elementary, + boson_elementary_expected * boson_elementary_expected); + checkProductEquals(matrix_elementary * spin_elementary, + matrix_elementary_expected * spin_elementary_expected); + checkProductEquals(spin_elementary * matrix_elementary, + spin_elementary_expected * matrix_elementary_expected); + checkProductEquals(matrix_elementary * boson_elementary, + matrix_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * matrix_elementary, + boson_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * boson_elementary, + spin_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * spin_elementary, + boson_elementary_expected * spin_elementary_expected); } // `elementary *= elementary` { auto matrix_product = cudaq::product_operator(matrix_elementary); matrix_product *= matrix_elementary; - checkProductEquals(matrix_product, matrix_elementary_expected * matrix_elementary_expected); + checkProductEquals(matrix_product, + matrix_elementary_expected * matrix_elementary_expected); auto spin_product = cudaq::product_operator(spin_elementary); spin_product *= spin_elementary; - checkProductEquals(spin_product, spin_elementary_expected * spin_elementary_expected, true); + checkProductEquals(spin_product, + spin_elementary_expected * spin_elementary_expected, + true); auto boson_product = cudaq::product_operator(boson_elementary); boson_product *= boson_elementary; - checkProductEquals(boson_product, boson_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_product, + boson_elementary_expected * boson_elementary_expected); matrix_product = cudaq::product_operator(matrix_elementary); matrix_product *= spin_elementary; - checkProductEquals(matrix_product, matrix_elementary_expected * spin_elementary_expected); + checkProductEquals(matrix_product, + matrix_elementary_expected * spin_elementary_expected); matrix_product = cudaq::product_operator(matrix_elementary); matrix_product *= boson_elementary; - checkProductEquals(matrix_product, matrix_elementary_expected * boson_elementary_expected); + checkProductEquals(matrix_product, + matrix_elementary_expected * boson_elementary_expected); } } TEST(OperatorExpressions, checkProductOperatorConversions) { - std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map> parameters = { + {"squeezing", 0.5}, {"displacement", 0.25}}; std::unordered_map dimensions = {{0, 2}, {1, 2}}; - auto matrix_product = cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); - auto matrix_product_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); + auto matrix_product = + cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); + auto matrix_product_expected = cudaq::kronecker( + utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); auto spin_product = cudaq::spin_operator::y(1) * cudaq::spin_operator::x(0); - auto spin_product_expected = cudaq::kronecker(utils::PauliY_matrix(), utils::PauliX_matrix()); - auto boson_product = cudaq::boson_operator::annihilate(1) * cudaq::boson_operator::number(0); - auto boson_product_expected = cudaq::kronecker(utils::annihilate_matrix(2), utils::number_matrix(2)); - - auto checkSumEquals = [dimensions, parameters]( - cudaq::operator_sum sum, - cudaq::matrix_2 expected, int expected_num_terms = 2) { - auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; - - auto checkProductEquals = [dimensions, parameters]( - cudaq::product_operator prod, - cudaq::matrix_2 expected, bool aggregated_prod = false) { - auto expected_num_terms = 4; - if (aggregated_prod) expected_num_terms = prod.degrees().size(); - auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; + auto spin_product_expected = + cudaq::kronecker(utils::PauliY_matrix(), utils::PauliX_matrix()); + auto boson_product = + cudaq::boson_operator::annihilate(1) * cudaq::boson_operator::number(0); + auto boson_product_expected = + cudaq::kronecker(utils::annihilate_matrix(2), utils::number_matrix(2)); + + auto checkSumEquals = + [dimensions, parameters](cudaq::operator_sum sum, + cudaq::matrix_2 expected, + int expected_num_terms = 2) { + auto got = sum.to_matrix(dimensions, parameters); + ASSERT_TRUE(sum.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; + + auto checkProductEquals = + [dimensions, + parameters](cudaq::product_operator prod, + cudaq::matrix_2 expected, bool aggregated_prod = false) { + auto expected_num_terms = 4; + if (aggregated_prod) + expected_num_terms = prod.degrees().size(); + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; // `product + product` { - checkSumEquals(matrix_product + matrix_product, matrix_product_expected + matrix_product_expected, 1); - checkSumEquals(spin_product + spin_product, spin_product_expected + spin_product_expected, 1); - checkSumEquals(boson_product + boson_product, boson_product_expected + boson_product_expected, 1); - checkSumEquals(matrix_product + spin_product, matrix_product_expected + spin_product_expected); - checkSumEquals(spin_product + matrix_product, matrix_product_expected + spin_product_expected); - checkSumEquals(matrix_product + boson_product, matrix_product_expected + boson_product_expected); - checkSumEquals(boson_product + matrix_product, matrix_product_expected + boson_product_expected); - checkSumEquals(spin_product + boson_product, spin_product_expected + boson_product_expected); - checkSumEquals(boson_product + spin_product, spin_product_expected + boson_product_expected); + checkSumEquals(matrix_product + matrix_product, + matrix_product_expected + matrix_product_expected, 1); + checkSumEquals(spin_product + spin_product, + spin_product_expected + spin_product_expected, 1); + checkSumEquals(boson_product + boson_product, + boson_product_expected + boson_product_expected, 1); + checkSumEquals(matrix_product + spin_product, + matrix_product_expected + spin_product_expected); + checkSumEquals(spin_product + matrix_product, + matrix_product_expected + spin_product_expected); + checkSumEquals(matrix_product + boson_product, + matrix_product_expected + boson_product_expected); + checkSumEquals(boson_product + matrix_product, + matrix_product_expected + boson_product_expected); + checkSumEquals(spin_product + boson_product, + spin_product_expected + boson_product_expected); + checkSumEquals(boson_product + spin_product, + spin_product_expected + boson_product_expected); } // `product - product` { - checkSumEquals(matrix_product - matrix_product, matrix_product_expected - matrix_product_expected, 1); - checkSumEquals(spin_product - spin_product, spin_product_expected - spin_product_expected, 1); - checkSumEquals(boson_product - boson_product, boson_product_expected - boson_product_expected, 1); - checkSumEquals(matrix_product - spin_product, matrix_product_expected - spin_product_expected); - checkSumEquals(spin_product - matrix_product, spin_product_expected - matrix_product_expected); - checkSumEquals(matrix_product - boson_product, matrix_product_expected - boson_product_expected); - checkSumEquals(boson_product - matrix_product, boson_product_expected - matrix_product_expected); - checkSumEquals(spin_product - boson_product, spin_product_expected - boson_product_expected); - checkSumEquals(boson_product - spin_product, boson_product_expected - spin_product_expected); + checkSumEquals(matrix_product - matrix_product, + matrix_product_expected - matrix_product_expected, 1); + checkSumEquals(spin_product - spin_product, + spin_product_expected - spin_product_expected, 1); + checkSumEquals(boson_product - boson_product, + boson_product_expected - boson_product_expected, 1); + checkSumEquals(matrix_product - spin_product, + matrix_product_expected - spin_product_expected); + checkSumEquals(spin_product - matrix_product, + spin_product_expected - matrix_product_expected); + checkSumEquals(matrix_product - boson_product, + matrix_product_expected - boson_product_expected); + checkSumEquals(boson_product - matrix_product, + boson_product_expected - matrix_product_expected); + checkSumEquals(spin_product - boson_product, + spin_product_expected - boson_product_expected); + checkSumEquals(boson_product - spin_product, + boson_product_expected - spin_product_expected); } // `product * product` { - checkProductEquals(matrix_product * matrix_product, matrix_product_expected * matrix_product_expected); - checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected, true); - checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected); - checkProductEquals(matrix_product * spin_product, matrix_product_expected * spin_product_expected); - checkProductEquals(spin_product * matrix_product, spin_product_expected * matrix_product_expected); - checkProductEquals(matrix_product * boson_product, matrix_product_expected * boson_product_expected); - checkProductEquals(boson_product * matrix_product, boson_product_expected * matrix_product_expected); - checkProductEquals(spin_product * boson_product, spin_product_expected * boson_product_expected); - checkProductEquals(boson_product * spin_product, boson_product_expected * spin_product_expected); + checkProductEquals(matrix_product * matrix_product, + matrix_product_expected * matrix_product_expected); + checkProductEquals(spin_product * spin_product, + spin_product_expected * spin_product_expected, true); + checkProductEquals(boson_product * boson_product, + boson_product_expected * boson_product_expected); + checkProductEquals(matrix_product * spin_product, + matrix_product_expected * spin_product_expected); + checkProductEquals(spin_product * matrix_product, + spin_product_expected * matrix_product_expected); + checkProductEquals(matrix_product * boson_product, + matrix_product_expected * boson_product_expected); + checkProductEquals(boson_product * matrix_product, + boson_product_expected * matrix_product_expected); + checkProductEquals(spin_product * boson_product, + spin_product_expected * boson_product_expected); + checkProductEquals(boson_product * spin_product, + boson_product_expected * spin_product_expected); } // `product *= product` { auto matrix_product_0 = matrix_product; matrix_product_0 *= matrix_product; - checkProductEquals(matrix_product_0, matrix_product_expected * matrix_product_expected); + checkProductEquals(matrix_product_0, + matrix_product_expected * matrix_product_expected); auto spin_product_0 = spin_product; spin_product_0 *= spin_product; - checkProductEquals(spin_product_0, spin_product_expected * spin_product_expected, true); + checkProductEquals(spin_product_0, + spin_product_expected * spin_product_expected, true); auto boson_product_0 = boson_product; boson_product_0 *= boson_product; - checkProductEquals(boson_product_0, boson_product_expected * boson_product_expected); + checkProductEquals(boson_product_0, + boson_product_expected * boson_product_expected); matrix_product_0 = matrix_product; matrix_product_0 *= spin_product; - checkProductEquals(matrix_product_0, matrix_product_expected * spin_product_expected); + checkProductEquals(matrix_product_0, + matrix_product_expected * spin_product_expected); matrix_product_0 = matrix_product; matrix_product_0 *= boson_product; - checkProductEquals(matrix_product_0, matrix_product_expected * boson_product_expected); + checkProductEquals(matrix_product_0, + matrix_product_expected * boson_product_expected); } } TEST(OperatorExpressions, checkOperatorSumConversions) { - std::unordered_map> parameters = {{"squeezing", 0.5}, {"displacement", 0.25}}; + std::unordered_map> parameters = { + {"squeezing", 0.5}, {"displacement", 0.25}}; std::unordered_map dimensions = {{0, 2}, {1, 2}}; - auto matrix_product = cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); - auto matrix_product_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); + auto matrix_product = + cudaq::matrix_operator::squeeze(0) * cudaq::matrix_operator::displace(1); + auto matrix_product_expected = cudaq::kronecker( + utils::displace_matrix(2, 0.25), utils::squeeze_matrix(2, 0.5)); auto spin_product = cudaq::spin_operator::y(1) * cudaq::spin_operator::x(0); - auto spin_product_expected = cudaq::kronecker(utils::PauliY_matrix(), utils::PauliX_matrix()); - auto boson_product = cudaq::boson_operator::annihilate(1) * cudaq::boson_operator::number(0); - auto boson_product_expected = cudaq::kronecker(utils::annihilate_matrix(2), utils::number_matrix(2)); - - auto matrix_sum = cudaq::matrix_operator::squeeze(0) + cudaq::matrix_operator::displace(1); - auto matrix_sum_expected = cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::id_matrix(2)) + - cudaq::kronecker(utils::id_matrix(2), utils::squeeze_matrix(2, 0.5)); + auto spin_product_expected = + cudaq::kronecker(utils::PauliY_matrix(), utils::PauliX_matrix()); + auto boson_product = + cudaq::boson_operator::annihilate(1) * cudaq::boson_operator::number(0); + auto boson_product_expected = + cudaq::kronecker(utils::annihilate_matrix(2), utils::number_matrix(2)); + + auto matrix_sum = + cudaq::matrix_operator::squeeze(0) + cudaq::matrix_operator::displace(1); + auto matrix_sum_expected = + cudaq::kronecker(utils::displace_matrix(2, 0.25), utils::id_matrix(2)) + + cudaq::kronecker(utils::id_matrix(2), utils::squeeze_matrix(2, 0.5)); auto spin_sum = cudaq::spin_operator::y(1) + cudaq::spin_operator::x(0); - auto spin_sum_expected = cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)) + - cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); - auto boson_sum = cudaq::boson_operator::annihilate(1) + cudaq::boson_operator::number(0); - auto boson_sum_expected = cudaq::kronecker(utils::annihilate_matrix(2), utils::id_matrix(2)) + - cudaq::kronecker(utils::id_matrix(2), utils::number_matrix(2)); - - auto checkSumEquals = [dimensions, parameters]( - cudaq::operator_sum sum, - cudaq::matrix_2 expected, int num_terms = 4) { - auto got = sum.to_matrix(dimensions, parameters); - ASSERT_TRUE(sum.num_terms() == num_terms); - utils::checkEqual(got, expected); - }; + auto spin_sum_expected = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)) + + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto boson_sum = + cudaq::boson_operator::annihilate(1) + cudaq::boson_operator::number(0); + auto boson_sum_expected = + cudaq::kronecker(utils::annihilate_matrix(2), utils::id_matrix(2)) + + cudaq::kronecker(utils::id_matrix(2), utils::number_matrix(2)); + + auto checkSumEquals = + [dimensions, parameters](cudaq::operator_sum sum, + cudaq::matrix_2 expected, int num_terms = 4) { + auto got = sum.to_matrix(dimensions, parameters); + ASSERT_TRUE(sum.num_terms() == num_terms); + utils::checkEqual(got, expected); + }; // `sum + product` { - checkSumEquals(matrix_sum + matrix_product, matrix_sum_expected + matrix_product_expected, 3); - checkSumEquals(spin_sum + spin_product, spin_sum_expected + spin_product_expected, 3); - checkSumEquals(boson_sum + boson_product, boson_sum_expected + boson_product_expected, 3); - checkSumEquals(matrix_sum + spin_product, matrix_sum_expected + spin_product_expected, 3); - checkSumEquals(spin_sum + matrix_product, spin_sum_expected + matrix_product_expected, 3); - checkSumEquals(matrix_sum + boson_product, matrix_sum_expected + boson_product_expected, 3); - checkSumEquals(boson_sum + matrix_product, boson_sum_expected + matrix_product_expected, 3); - checkSumEquals(spin_sum + boson_product, spin_sum_expected + boson_product_expected, 3); - checkSumEquals(boson_sum + spin_product, boson_sum_expected + spin_product_expected, 3); + checkSumEquals(matrix_sum + matrix_product, + matrix_sum_expected + matrix_product_expected, 3); + checkSumEquals(spin_sum + spin_product, + spin_sum_expected + spin_product_expected, 3); + checkSumEquals(boson_sum + boson_product, + boson_sum_expected + boson_product_expected, 3); + checkSumEquals(matrix_sum + spin_product, + matrix_sum_expected + spin_product_expected, 3); + checkSumEquals(spin_sum + matrix_product, + spin_sum_expected + matrix_product_expected, 3); + checkSumEquals(matrix_sum + boson_product, + matrix_sum_expected + boson_product_expected, 3); + checkSumEquals(boson_sum + matrix_product, + boson_sum_expected + matrix_product_expected, 3); + checkSumEquals(spin_sum + boson_product, + spin_sum_expected + boson_product_expected, 3); + checkSumEquals(boson_sum + spin_product, + boson_sum_expected + spin_product_expected, 3); } // `product + sum` { - checkSumEquals(matrix_product + matrix_sum, matrix_product_expected + matrix_sum_expected, 3); - checkSumEquals(spin_product + spin_sum, spin_product_expected + spin_sum_expected, 3); - checkSumEquals(boson_product + boson_sum, boson_product_expected + boson_sum_expected, 3); - checkSumEquals(matrix_product + spin_sum, matrix_product_expected + spin_sum_expected, 3); - checkSumEquals(spin_product + matrix_sum, spin_product_expected + matrix_sum_expected, 3); - checkSumEquals(matrix_product + boson_sum, matrix_product_expected + boson_sum_expected, 3); - checkSumEquals(boson_product + matrix_sum, boson_product_expected + matrix_sum_expected, 3); - checkSumEquals(spin_product + boson_sum, spin_product_expected + boson_sum_expected, 3); - checkSumEquals(boson_product + spin_sum, boson_product_expected + spin_sum_expected, 3); + checkSumEquals(matrix_product + matrix_sum, + matrix_product_expected + matrix_sum_expected, 3); + checkSumEquals(spin_product + spin_sum, + spin_product_expected + spin_sum_expected, 3); + checkSumEquals(boson_product + boson_sum, + boson_product_expected + boson_sum_expected, 3); + checkSumEquals(matrix_product + spin_sum, + matrix_product_expected + spin_sum_expected, 3); + checkSumEquals(spin_product + matrix_sum, + spin_product_expected + matrix_sum_expected, 3); + checkSumEquals(matrix_product + boson_sum, + matrix_product_expected + boson_sum_expected, 3); + checkSumEquals(boson_product + matrix_sum, + boson_product_expected + matrix_sum_expected, 3); + checkSumEquals(spin_product + boson_sum, + spin_product_expected + boson_sum_expected, 3); + checkSumEquals(boson_product + spin_sum, + boson_product_expected + spin_sum_expected, 3); } // `sum + sum` { - checkSumEquals(matrix_sum + matrix_sum, matrix_sum_expected + matrix_sum_expected, 2); - checkSumEquals(spin_sum + spin_sum, spin_sum_expected + spin_sum_expected, 2); - checkSumEquals(boson_sum + boson_sum, boson_sum_expected + boson_sum_expected, 2); - checkSumEquals(matrix_sum + spin_sum, matrix_sum_expected + spin_sum_expected); - checkSumEquals(spin_sum + matrix_sum, matrix_sum_expected + spin_sum_expected); - checkSumEquals(matrix_sum + boson_sum, matrix_sum_expected + boson_sum_expected); - checkSumEquals(boson_sum + matrix_sum, matrix_sum_expected + boson_sum_expected); - checkSumEquals(spin_sum + boson_sum, spin_sum_expected + boson_sum_expected); - checkSumEquals(boson_sum + spin_sum, spin_sum_expected + boson_sum_expected); + checkSumEquals(matrix_sum + matrix_sum, + matrix_sum_expected + matrix_sum_expected, 2); + checkSumEquals(spin_sum + spin_sum, spin_sum_expected + spin_sum_expected, + 2); + checkSumEquals(boson_sum + boson_sum, + boson_sum_expected + boson_sum_expected, 2); + checkSumEquals(matrix_sum + spin_sum, + matrix_sum_expected + spin_sum_expected); + checkSumEquals(spin_sum + matrix_sum, + matrix_sum_expected + spin_sum_expected); + checkSumEquals(matrix_sum + boson_sum, + matrix_sum_expected + boson_sum_expected); + checkSumEquals(boson_sum + matrix_sum, + matrix_sum_expected + boson_sum_expected); + checkSumEquals(spin_sum + boson_sum, + spin_sum_expected + boson_sum_expected); + checkSumEquals(boson_sum + spin_sum, + spin_sum_expected + boson_sum_expected); } // `sum - product` { - checkSumEquals(matrix_sum - matrix_product, matrix_sum_expected - matrix_product_expected, 3); - checkSumEquals(spin_sum - spin_product, spin_sum_expected - spin_product_expected, 3); - checkSumEquals(boson_sum - boson_product, boson_sum_expected - boson_product_expected, 3); - checkSumEquals(matrix_sum - spin_product, matrix_sum_expected - spin_product_expected, 3); - checkSumEquals(spin_sum - matrix_product, spin_sum_expected - matrix_product_expected, 3); - checkSumEquals(matrix_sum - boson_product, matrix_sum_expected - boson_product_expected, 3); - checkSumEquals(boson_sum - matrix_product, boson_sum_expected - matrix_product_expected, 3); - checkSumEquals(spin_sum - boson_product, spin_sum_expected - boson_product_expected, 3); - checkSumEquals(boson_sum - spin_product, boson_sum_expected - spin_product_expected, 3); + checkSumEquals(matrix_sum - matrix_product, + matrix_sum_expected - matrix_product_expected, 3); + checkSumEquals(spin_sum - spin_product, + spin_sum_expected - spin_product_expected, 3); + checkSumEquals(boson_sum - boson_product, + boson_sum_expected - boson_product_expected, 3); + checkSumEquals(matrix_sum - spin_product, + matrix_sum_expected - spin_product_expected, 3); + checkSumEquals(spin_sum - matrix_product, + spin_sum_expected - matrix_product_expected, 3); + checkSumEquals(matrix_sum - boson_product, + matrix_sum_expected - boson_product_expected, 3); + checkSumEquals(boson_sum - matrix_product, + boson_sum_expected - matrix_product_expected, 3); + checkSumEquals(spin_sum - boson_product, + spin_sum_expected - boson_product_expected, 3); + checkSumEquals(boson_sum - spin_product, + boson_sum_expected - spin_product_expected, 3); } // `product - sum` { - checkSumEquals(matrix_product - matrix_sum, matrix_product_expected - matrix_sum_expected, 3); - checkSumEquals(spin_product - spin_sum, spin_product_expected - spin_sum_expected, 3); - checkSumEquals(boson_product - boson_sum, boson_product_expected - boson_sum_expected, 3); - checkSumEquals(matrix_product - spin_sum, matrix_product_expected - spin_sum_expected, 3); - checkSumEquals(spin_product - matrix_sum, spin_product_expected - matrix_sum_expected, 3); - checkSumEquals(matrix_product - boson_sum, matrix_product_expected - boson_sum_expected, 3); - checkSumEquals(boson_product - matrix_sum, boson_product_expected - matrix_sum_expected, 3); - checkSumEquals(spin_product - boson_sum, spin_product_expected - boson_sum_expected, 3); - checkSumEquals(boson_product - spin_sum, boson_product_expected - spin_sum_expected, 3); + checkSumEquals(matrix_product - matrix_sum, + matrix_product_expected - matrix_sum_expected, 3); + checkSumEquals(spin_product - spin_sum, + spin_product_expected - spin_sum_expected, 3); + checkSumEquals(boson_product - boson_sum, + boson_product_expected - boson_sum_expected, 3); + checkSumEquals(matrix_product - spin_sum, + matrix_product_expected - spin_sum_expected, 3); + checkSumEquals(spin_product - matrix_sum, + spin_product_expected - matrix_sum_expected, 3); + checkSumEquals(matrix_product - boson_sum, + matrix_product_expected - boson_sum_expected, 3); + checkSumEquals(boson_product - matrix_sum, + boson_product_expected - matrix_sum_expected, 3); + checkSumEquals(spin_product - boson_sum, + spin_product_expected - boson_sum_expected, 3); + checkSumEquals(boson_product - spin_sum, + boson_product_expected - spin_sum_expected, 3); } // `sum - sum` { - checkSumEquals(matrix_sum - matrix_sum, matrix_sum_expected - matrix_sum_expected, 2); - checkSumEquals(spin_sum - spin_sum, spin_sum_expected - spin_sum_expected, 2); - checkSumEquals(boson_sum - boson_sum, boson_sum_expected - boson_sum_expected, 2); - checkSumEquals(matrix_sum - spin_sum, matrix_sum_expected - spin_sum_expected); - checkSumEquals(spin_sum - matrix_sum, spin_sum_expected - matrix_sum_expected); - checkSumEquals(matrix_sum - boson_sum, matrix_sum_expected - boson_sum_expected); - checkSumEquals(boson_sum - matrix_sum, boson_sum_expected - matrix_sum_expected); - checkSumEquals(spin_sum - boson_sum, spin_sum_expected - boson_sum_expected); - checkSumEquals(boson_sum - spin_sum, boson_sum_expected - spin_sum_expected); + checkSumEquals(matrix_sum - matrix_sum, + matrix_sum_expected - matrix_sum_expected, 2); + checkSumEquals(spin_sum - spin_sum, spin_sum_expected - spin_sum_expected, + 2); + checkSumEquals(boson_sum - boson_sum, + boson_sum_expected - boson_sum_expected, 2); + checkSumEquals(matrix_sum - spin_sum, + matrix_sum_expected - spin_sum_expected); + checkSumEquals(spin_sum - matrix_sum, + spin_sum_expected - matrix_sum_expected); + checkSumEquals(matrix_sum - boson_sum, + matrix_sum_expected - boson_sum_expected); + checkSumEquals(boson_sum - matrix_sum, + boson_sum_expected - matrix_sum_expected); + checkSumEquals(spin_sum - boson_sum, + spin_sum_expected - boson_sum_expected); + checkSumEquals(boson_sum - spin_sum, + boson_sum_expected - spin_sum_expected); } // `sum * product` { - checkSumEquals(matrix_sum * matrix_product, matrix_sum_expected * matrix_product_expected, 2); - checkSumEquals(spin_sum * spin_product, spin_sum_expected * spin_product_expected, 2); - checkSumEquals(boson_sum * boson_product, boson_sum_expected * boson_product_expected, 2); - checkSumEquals(matrix_sum * spin_product, matrix_sum_expected * spin_product_expected, 2); - checkSumEquals(spin_sum * matrix_product, spin_sum_expected * matrix_product_expected, 2); - checkSumEquals(matrix_sum * boson_product, matrix_sum_expected * boson_product_expected, 2); - checkSumEquals(boson_sum * matrix_product, boson_sum_expected * matrix_product_expected, 2); - checkSumEquals(spin_sum * boson_product, spin_sum_expected * boson_product_expected, 2); - checkSumEquals(boson_sum * spin_product, boson_sum_expected * spin_product_expected, 2); + checkSumEquals(matrix_sum * matrix_product, + matrix_sum_expected * matrix_product_expected, 2); + checkSumEquals(spin_sum * spin_product, + spin_sum_expected * spin_product_expected, 2); + checkSumEquals(boson_sum * boson_product, + boson_sum_expected * boson_product_expected, 2); + checkSumEquals(matrix_sum * spin_product, + matrix_sum_expected * spin_product_expected, 2); + checkSumEquals(spin_sum * matrix_product, + spin_sum_expected * matrix_product_expected, 2); + checkSumEquals(matrix_sum * boson_product, + matrix_sum_expected * boson_product_expected, 2); + checkSumEquals(boson_sum * matrix_product, + boson_sum_expected * matrix_product_expected, 2); + checkSumEquals(spin_sum * boson_product, + spin_sum_expected * boson_product_expected, 2); + checkSumEquals(boson_sum * spin_product, + boson_sum_expected * spin_product_expected, 2); } // `product * sum` { - checkSumEquals(matrix_product * matrix_sum, matrix_product_expected * matrix_sum_expected, 2); - checkSumEquals(spin_product * spin_sum, spin_product_expected * spin_sum_expected, 2); - checkSumEquals(boson_product * boson_sum, boson_product_expected * boson_sum_expected, 2); - checkSumEquals(matrix_product * spin_sum, matrix_product_expected * spin_sum_expected, 2); - checkSumEquals(spin_product * matrix_sum, spin_product_expected * matrix_sum_expected, 2); - checkSumEquals(matrix_product * boson_sum, matrix_product_expected * boson_sum_expected, 2); - checkSumEquals(boson_product * matrix_sum, boson_product_expected * matrix_sum_expected, 2); - checkSumEquals(spin_product * boson_sum, spin_product_expected * boson_sum_expected, 2); - checkSumEquals(boson_product * spin_sum, boson_product_expected * spin_sum_expected, 2); + checkSumEquals(matrix_product * matrix_sum, + matrix_product_expected * matrix_sum_expected, 2); + checkSumEquals(spin_product * spin_sum, + spin_product_expected * spin_sum_expected, 2); + checkSumEquals(boson_product * boson_sum, + boson_product_expected * boson_sum_expected, 2); + checkSumEquals(matrix_product * spin_sum, + matrix_product_expected * spin_sum_expected, 2); + checkSumEquals(spin_product * matrix_sum, + spin_product_expected * matrix_sum_expected, 2); + checkSumEquals(matrix_product * boson_sum, + matrix_product_expected * boson_sum_expected, 2); + checkSumEquals(boson_product * matrix_sum, + boson_product_expected * matrix_sum_expected, 2); + checkSumEquals(spin_product * boson_sum, + spin_product_expected * boson_sum_expected, 2); + checkSumEquals(boson_product * spin_sum, + boson_product_expected * spin_sum_expected, 2); } // `sum * sum` { - checkSumEquals(matrix_sum * matrix_sum, matrix_sum_expected * matrix_sum_expected, 3); - checkSumEquals(spin_sum * spin_sum, spin_sum_expected * spin_sum_expected, 3); - checkSumEquals(boson_sum * boson_sum, boson_sum_expected * boson_sum_expected, 3); - checkSumEquals(matrix_sum * spin_sum, matrix_sum_expected * spin_sum_expected); - checkSumEquals(spin_sum * matrix_sum, spin_sum_expected * matrix_sum_expected); - checkSumEquals(matrix_sum * boson_sum, matrix_sum_expected * boson_sum_expected); - checkSumEquals(boson_sum * matrix_sum, boson_sum_expected * matrix_sum_expected); - checkSumEquals(spin_sum * boson_sum, spin_sum_expected * boson_sum_expected); - checkSumEquals(boson_sum * spin_sum, boson_sum_expected * spin_sum_expected); + checkSumEquals(matrix_sum * matrix_sum, + matrix_sum_expected * matrix_sum_expected, 3); + checkSumEquals(spin_sum * spin_sum, spin_sum_expected * spin_sum_expected, + 3); + checkSumEquals(boson_sum * boson_sum, + boson_sum_expected * boson_sum_expected, 3); + checkSumEquals(matrix_sum * spin_sum, + matrix_sum_expected * spin_sum_expected); + checkSumEquals(spin_sum * matrix_sum, + spin_sum_expected * matrix_sum_expected); + checkSumEquals(matrix_sum * boson_sum, + matrix_sum_expected * boson_sum_expected); + checkSumEquals(boson_sum * matrix_sum, + boson_sum_expected * matrix_sum_expected); + checkSumEquals(spin_sum * boson_sum, + spin_sum_expected * boson_sum_expected); + checkSumEquals(boson_sum * spin_sum, + boson_sum_expected * spin_sum_expected); } // `sum += product` { auto matrix_sum_0 = matrix_sum; matrix_sum_0 += matrix_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected + matrix_product_expected, 3); + checkSumEquals(matrix_sum_0, matrix_sum_expected + matrix_product_expected, + 3); auto spin_sum_0 = spin_sum; spin_sum_0 += spin_product; @@ -358,11 +530,13 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { matrix_sum_0 = matrix_sum; matrix_sum_0 += spin_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected + spin_product_expected, 3); + checkSumEquals(matrix_sum_0, matrix_sum_expected + spin_product_expected, + 3); matrix_sum_0 = matrix_sum; matrix_sum_0 += boson_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected + boson_product_expected, 3); + checkSumEquals(matrix_sum_0, matrix_sum_expected + boson_product_expected, + 3); } // `sum += sum` @@ -392,7 +566,8 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { { auto matrix_sum_0 = matrix_sum; matrix_sum_0 -= matrix_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected - matrix_product_expected, 3); + checkSumEquals(matrix_sum_0, matrix_sum_expected - matrix_product_expected, + 3); auto spin_sum_0 = spin_sum; spin_sum_0 -= spin_product; @@ -404,11 +579,13 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { matrix_sum_0 = matrix_sum; matrix_sum_0 -= spin_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected - spin_product_expected, 3); + checkSumEquals(matrix_sum_0, matrix_sum_expected - spin_product_expected, + 3); matrix_sum_0 = matrix_sum; matrix_sum_0 -= boson_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected - boson_product_expected, 3); + checkSumEquals(matrix_sum_0, matrix_sum_expected - boson_product_expected, + 3); } // `sum -= sum` @@ -438,7 +615,8 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { { auto matrix_sum_0 = matrix_sum; matrix_sum_0 *= matrix_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected * matrix_product_expected, 2); + checkSumEquals(matrix_sum_0, matrix_sum_expected * matrix_product_expected, + 2); auto spin_sum_0 = spin_sum; spin_sum_0 *= spin_product; @@ -450,11 +628,13 @@ TEST(OperatorExpressions, checkOperatorSumConversions) { matrix_sum_0 = matrix_sum; matrix_sum_0 *= spin_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected * spin_product_expected, 2); + checkSumEquals(matrix_sum_0, matrix_sum_expected * spin_product_expected, + 2); matrix_sum_0 = matrix_sum; matrix_sum_0 *= boson_product; - checkSumEquals(matrix_sum_0, matrix_sum_expected * boson_product_expected, 2); + checkSumEquals(matrix_sum_0, matrix_sum_expected * boson_product_expected, + 2); } // `sum *= sum` diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 86740b1e1b..341edd32df 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -6,10 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "utils.h" #include "cudaq/operators.h" +#include "utils.h" #include -#include "cudaq/dynamics/spin_operators.h" TEST(OperatorExpressions, checkOperatorSumBasics) { std::vector levels = {2, 3, 4}; @@ -19,238 +18,248 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - { - // Same degrees of freedom. - { - auto spin0 = cudaq::spin_operator::x(5); - auto spin1 = cudaq::spin_operator::z(5); - auto spin_sum = spin0 + spin1; - - std::vector want_degrees = {5}; - auto spin_matrix = utils::PauliX_matrix() + utils::PauliZ_matrix(); - - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(5); - auto op1 = cudaq::matrix_operator::create(5); - - auto sum = op0 + op1; - ASSERT_TRUE(sum.degrees() == want_degrees); - - auto got_matrix = sum.to_matrix({{5, level_count}}); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); - auto want_matrix = matrix0 + matrix1; - utils::checkEqual(want_matrix, got_matrix); - } - } + {// Same degrees of freedom. + {auto spin0 = cudaq::spin_operator::x(5); + auto spin1 = cudaq::spin_operator::z(5); + auto spin_sum = spin0 + spin1; - // Different degrees of freedom. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(1); - auto spin_sum = spin0 + spin1; + std::vector want_degrees = {5}; + auto spin_matrix = utils::PauliX_matrix() + utils::PauliZ_matrix(); - std::vector want_degrees = {1, 0}; - auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + - cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(5); + auto op1 = cudaq::matrix_operator::create(5); - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(1); + auto sum = op0 + op1; + ASSERT_TRUE(sum.degrees() == want_degrees); - auto got = op0 + op1; - auto got_reverse = op1 + op0; + auto got_matrix = sum.to_matrix({{5, level_count}}); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + auto want_matrix = matrix0 + matrix1; + utils::checkEqual(want_matrix, got_matrix); + } +} - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); +// Different degrees of freedom. +{ + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(1); + auto spin_sum = spin0 + spin1; - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + std::vector want_degrees = {1, 0}; + auto spin_matrix = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 + fullHilbert1; + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(1); - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix, got_matrix_reverse); - } - } + auto got = op0 + op1; + auto got_reverse = op1 + op0; - // Different degrees of freedom, non-consecutive. - // Should produce the same matrices as the above test. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_sum = spin0 + spin1; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + - cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 + fullHilbert1; - auto got = op0 + op1; - auto got_reverse = op1 + op0; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } +} - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); +// Different degrees of freedom, non-consecutive. +// Should produce the same matrices as the above test. +{ + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_sum = spin0 + spin1; - auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + std::vector want_degrees = {2, 0}; + auto spin_matrix = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 + fullHilbert1; + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix, got_matrix_reverse); - } - } + auto got = op0 + op1; + auto got_reverse = op1 + op0; - // Different degrees of freedom, non-consecutive but all dimensions - // provided. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_sum = spin0 + spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + - cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; - - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); - - auto got = op0 + op1; - auto got_reverse = op1 + op0; - - std::vector want_degrees = {2, 0}; - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - dimensions = {{0, level_count},{1, level_count},{2, level_count}}; - auto got_matrix = got.to_matrix(dimensions); - auto got_matrix_reverse = got_reverse.to_matrix(dimensions); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); - std::vector matrices_0 = {identity, matrix0}; - std::vector matrices_1 = {matrix1, identity}; - - auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - auto fullHilbert1 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto want_matrix = fullHilbert0 + fullHilbert1; - auto want_matrix_reverse = fullHilbert1 + fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(got_matrix, want_matrix); - } - } + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0, level_count}, {2, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {2, level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 + fullHilbert1; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); } +} + +// Different degrees of freedom, non-consecutive but all dimensions +// provided. +{ + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + std::vector want_degrees = {2, 0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + dimensions = {{0, level_count}, {1, level_count}, {2, level_count}}; + auto got_matrix = got.to_matrix(dimensions); + auto got_matrix_reverse = got_reverse.to_matrix(dimensions); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + std::vector matrices_0 = {identity, matrix0}; + std::vector matrices_1 = {matrix1, identity}; - // Scalar Ops against Elementary Ops + auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 + fullHilbert1; + auto want_matrix_reverse = fullHilbert1 + fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(got_matrix, want_matrix); + } +} +} + +// Scalar Ops against Elementary Ops +{ + auto function = [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; + + // matrix operator against constant { - auto function = [](const std::unordered_map> ¶meters) { - auto entry = parameters.find("value"); - if (entry == parameters.end()) - throw std::runtime_error("value not defined in parameters"); - return entry->second; - }; - - // matrix operator against constant - { - auto op = cudaq::matrix_operator::annihilate(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::annihilate_matrix(2); - auto scalar_matrix = value_0 * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); - } + auto op = cudaq::matrix_operator::annihilate(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::annihilate_matrix(2); + auto scalar_matrix = value_0 * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); + } - // spin operator against constant - { - auto op = cudaq::spin_operator::x(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::PauliX_matrix(); - auto scalar_matrix = value_0 * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); - } + // spin operator against constant + { + auto op = cudaq::spin_operator::x(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = value_0 * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); + } - // matrix operator against constant from lambda - { - auto op = cudaq::matrix_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(function); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::annihilate_matrix(2); - auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); - } + // matrix operator against constant from lambda + { + auto op = cudaq::matrix_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(function); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::annihilate_matrix(2); + auto scalar_matrix = + scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, + sum.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_matrix + op_matrix, + reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); + } - // spin operator against constant from lambda - { - auto op = cudaq::spin_operator::x(1); - auto scalar_op = cudaq::scalar_operator(function); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::PauliX_matrix(); - auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); - } + // spin operator against constant from lambda + { + auto op = cudaq::spin_operator::x(1); + auto scalar_op = cudaq::scalar_operator(function); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = + scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, + sum.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_matrix + op_matrix, + reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); } } +} TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { int level_count = 3; @@ -268,14 +277,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::momentum_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -284,8 +295,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = original + value; auto reverse = value + original; @@ -293,14 +304,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -309,8 +322,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `spin sum + std::complex` { - auto original = cudaq::spin_operator::x(1) + - cudaq::spin_operator::y(2); + auto original = cudaq::spin_operator::x(1) + cudaq::spin_operator::y(2); auto sum = original + value; auto reverse = value + original; @@ -321,10 +333,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); - auto matrix0 = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto matrix1 = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + auto matrix0 = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto matrix1 = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto scaled_identity = value * utils::id_matrix(2 * 2); auto want_matrix = matrix0 + matrix1 + scaled_identity; @@ -335,8 +347,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + scalar_operator` { level_count = 2; - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -344,9 +356,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - - auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2,level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); @@ -364,8 +376,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - double` { - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::number(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::number(2); auto difference = original - double_value; auto reverse = double_value - original; @@ -373,15 +385,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; @@ -391,8 +406,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `spin sum - double` { - auto original = cudaq::spin_operator::x(1) + - cudaq::spin_operator::z(2); + auto original = cudaq::spin_operator::x(1) + cudaq::spin_operator::z(2); auto difference = original - double_value; auto reverse = double_value - original; @@ -403,10 +417,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = difference.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); - auto matrix0 = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto matrix1 = cudaq::kronecker(utils::PauliZ_matrix(), - utils::id_matrix(2)); + auto matrix0 = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto matrix1 = + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = double_value * utils::id_matrix(2 * 2); @@ -418,8 +432,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto difference = original - value; auto reverse = value - original; @@ -427,15 +441,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto got_matrix = difference.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; auto want_matrix_reverse = scaled_identity - sum_matrix; @@ -445,8 +462,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - scalar_operator` { - auto original = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto original = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -454,8 +471,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(difference.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto got_matrix = difference.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + difference.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); @@ -473,8 +492,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -484,22 +503,27 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { for (auto term : product.get_terms()) { ASSERT_TRUE(term.num_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } for (auto term : reverse.get_terms()) { ASSERT_TRUE(term.num_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } - auto got_matrix = product.to_matrix({{1, level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1, level_count}, {2, level_count+1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -508,8 +532,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); auto product = sum * value; auto reverse = value * sum; @@ -527,14 +551,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = product.to_matrix({{1,level_count}, {2, level_count+1}}); - auto got_matrix_reverse = reverse.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = + product.to_matrix({{1, level_count}, {2, level_count + 1}}); + auto got_matrix_reverse = + reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -583,8 +610,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `spin sum * scalar_operator` { - auto sum = cudaq::spin_operator::i(1) + - cudaq::spin_operator::y(2); + auto sum = cudaq::spin_operator::i(1) + cudaq::spin_operator::y(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -605,10 +631,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = product.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); - auto matrix0 = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(2)); - auto matrix1 = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); + auto matrix1 = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto scaled_identity = value * utils::id_matrix(2 * 2); auto want_matrix = (matrix0 + matrix1) * scaled_identity; @@ -618,27 +643,29 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= double` { - auto sum = cudaq::matrix_operator::squeeze(1) + - cudaq::matrix_operator::squeeze(2); + auto sum = + cudaq::matrix_operator::squeeze(1) + cudaq::matrix_operator::squeeze(2); sum *= double_value; ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.num_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"squeezing", value}}); - auto matrix0 = - cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::squeeze_matrix(level_count, value)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::squeeze_matrix(level_count, value)); auto matrix1 = cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix * scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -646,19 +673,20 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `spin sum *= double` { - auto sum = cudaq::spin_operator::y(1) + - cudaq::spin_operator::i(2); + auto sum = cudaq::spin_operator::y(1) + cudaq::spin_operator::i(2); sum *= double_value; ASSERT_TRUE(sum.num_terms() == 2); for (auto term : sum.get_terms()) { ASSERT_TRUE(term.num_terms() == 1); - ASSERT_TRUE(term.get_coefficient().evaluate() == std::complex(double_value)); + ASSERT_TRUE(term.get_coefficient().evaluate() == + std::complex(double_value)); } auto got_matrix = sum.to_matrix(); - auto matrix0 = cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); + auto matrix0 = + cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); auto matrix1 = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); auto scaled_identity = double_value * utils::id_matrix(2 * 2); auto want_matrix = (matrix0 + matrix1) * scaled_identity; @@ -668,8 +696,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= std::complex` { - auto sum = cudaq::matrix_operator::displace(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::displace(1) + cudaq::matrix_operator::parity(2); sum *= value; @@ -679,14 +707,15 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(term.get_coefficient().evaluate() == value); } - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"displacement", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"displacement", value}}); - auto matrix0 = - cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::displace_matrix(level_count, value)); + auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), + utils::displace_matrix(level_count, value)); auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -709,14 +738,13 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::create_matrix(level_count)}; std::vector matrices_2 = { - utils::momentum_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) * scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -724,20 +752,21 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum += double_value; ASSERT_TRUE(sum.num_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -745,17 +774,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `spin sum += double` { - auto sum = cudaq::spin_operator::y(1) + - cudaq::spin_operator::y(2); + auto sum = cudaq::spin_operator::y(1) + cudaq::spin_operator::y(2); sum += double_value; ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix({{1, 2}, {2, 2}}); - auto matrix0 = cudaq::kronecker(utils::id_matrix(2), - utils::PauliY_matrix()); - auto matrix1 = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + auto matrix0 = + cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); + auto matrix1 = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto scaled_identity = double_value * utils::id_matrix(2 * 2); auto want_matrix = matrix0 + matrix1 + scaled_identity; @@ -771,14 +799,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { ASSERT_TRUE(sum.num_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}, {{"squeezing", value}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}, + {{"squeezing", value}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::momentum_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::squeeze_matrix(level_count + 1, value), utils::id_matrix(level_count)); - auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -797,14 +827,13 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; std::vector matrices_2 = { - utils::position_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::position_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils::id_matrix((level_count + 1) * level_count); auto want_matrix = matrix0 + matrix1 + scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -812,14 +841,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum -= double_value; ASSERT_TRUE(sum.num_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::create_matrix(level_count)); @@ -827,7 +856,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; - auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + double_value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = sum_matrix - scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -835,20 +865,21 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= std::complex` { - auto sum = cudaq::matrix_operator::position(1) + - cudaq::matrix_operator::number(2); + auto sum = + cudaq::matrix_operator::position(1) + cudaq::matrix_operator::number(2); sum -= value; ASSERT_TRUE(sum.num_terms() == 3); - auto got_matrix = sum.to_matrix({{1,level_count}, {2, level_count+1}}); + auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), utils::position_matrix(level_count)); auto matrix1 = cudaq::kronecker(utils::number_matrix(level_count + 1), utils::id_matrix(level_count)); - auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); + auto scaled_identity = + value * utils::id_matrix((level_count) * (level_count + 1)); auto want_matrix = matrix0 + matrix1 - scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -867,14 +898,14 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils::id_matrix(level_count + 1), - utils::number_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::number_matrix(level_count)}; std::vector matrices_2 = { utils::annihilate_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); - auto scaled_identity = value * utils::id_matrix((level_count + 1) * level_count); + auto scaled_identity = + value * utils::id_matrix((level_count + 1) * level_count); auto want_matrix = (matrix0 + matrix1) - scaled_identity; utils::checkEqual(want_matrix, got_matrix); @@ -882,16 +913,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `spin sum -= scalar_operator` { - auto sum = cudaq::spin_operator::z(1) + - cudaq::spin_operator::y(2); + auto sum = cudaq::spin_operator::z(1) + cudaq::spin_operator::y(2); sum -= cudaq::scalar_operator(value); ASSERT_TRUE(sum.num_terms() == 3); auto got_matrix = sum.to_matrix(); - std::vector matrices_1 = {utils::id_matrix(2), utils::PauliZ_matrix()}; - std::vector matrices_2 = {utils::PauliY_matrix(), utils::id_matrix(2)}; + std::vector matrices_1 = {utils::id_matrix(2), + utils::PauliZ_matrix()}; + std::vector matrices_2 = {utils::PauliY_matrix(), + utils::id_matrix(2)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); auto scaled_identity = value * utils::id_matrix(2 * 2); @@ -914,17 +946,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum += product; ASSERT_TRUE(sum.num_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), + utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), @@ -933,12 +965,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -955,17 +985,17 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum -= product; ASSERT_TRUE(sum.num_terms() == 3); - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), + utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), @@ -974,12 +1004,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -996,8 +1024,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { { auto product = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::annihilate(1); - auto sum = cudaq::matrix_operator::create(1) + - cudaq::matrix_operator::create(2); + auto sum = + cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); sum *= product; @@ -1006,10 +1034,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { ASSERT_TRUE(term.num_terms() == 3); } - auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count+1}, {2, level_count+2}}); + auto got_matrix = sum.to_matrix( + {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), + utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), @@ -1018,12 +1046,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1064,18 +1090,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::create_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; + matrices_1_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::annihilate_matrix(level_count + 1), @@ -1120,18 +1144,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::position_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; + matrices_1_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::annihilate_matrix(level_count + 1), @@ -1187,18 +1209,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::create_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; + matrices_1_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::annihilate_matrix(level_count + 1), @@ -1247,18 +1267,16 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::create_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; + matrices_1_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::annihilate_matrix(level_count + 1), @@ -1282,75 +1300,82 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { } TEST(OperatorExpressions, checkCustomOperatorSum) { - auto level_count = 2; - std::unordered_map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; - - { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + auto level_count = 2; + std::unordered_map dimensions = {{0, level_count + 1}, + {1, level_count + 2}, + {2, level_count}, + {3, level_count + 3}}; - auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); - auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); - auto sum = op0 + op1; - auto sum_reverse = op1 + op0; - auto difference = op0 - op1; - auto difference_reverse = op1 - op0; + { + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); + auto sum = op0 + op1; + auto sum_reverse = op1 + op0; + auto difference = op0 - op1; + auto difference_reverse = op1 - op0; - std::vector matrices_0 = { - utils::id_matrix(level_count), - utils::position_matrix(level_count + 2), + std::vector matrices_0 = { + utils::id_matrix(level_count), utils::position_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; - std::vector matrices_1 = { - utils::number_matrix(level_count), - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1)}; - auto sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - - utils::checkEqual(sum.to_matrix(dimensions), sum_expected); - utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); - utils::checkEqual(difference.to_matrix(dimensions), diff_expected); - utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); - - op0 = cudaq::matrix_operator::instantiate("custom_op0", {2, 3}); - op1 = cudaq::matrix_operator::instantiate("custom_op1", {2, 0}); - sum = op0 + op1; - sum_reverse = op1 + op0; - difference = op0 - op1; - difference_reverse = op1 - op0; - - matrices_0 = { - utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count), + std::vector matrices_1 = { + utils::number_matrix(level_count), utils::create_matrix(level_count + 2), utils::id_matrix(level_count + 1)}; - matrices_1 = { - utils::id_matrix(level_count + 3), - utils::create_matrix(level_count), - utils::number_matrix(level_count + 1)}; - sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - - utils::checkEqual(sum.to_matrix(dimensions), sum_expected); - utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); - utils::checkEqual(difference.to_matrix(dimensions), diff_expected); - utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); + auto sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto diff_reverse_expected = + cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + + utils::checkEqual(sum.to_matrix(dimensions), sum_expected); + utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); + utils::checkEqual(difference.to_matrix(dimensions), diff_expected); + utils::checkEqual(difference_reverse.to_matrix(dimensions), + diff_reverse_expected); + + op0 = cudaq::matrix_operator::instantiate("custom_op0", {2, 3}); + op1 = cudaq::matrix_operator::instantiate("custom_op1", {2, 0}); + sum = op0 + op1; + sum_reverse = op1 + op0; + difference = op0 - op1; + difference_reverse = op1 - op0; + + matrices_0 = {utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count), + utils::id_matrix(level_count + 1)}; + matrices_1 = {utils::id_matrix(level_count + 3), + utils::create_matrix(level_count), + utils::number_matrix(level_count + 1)}; + sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_reverse_expected = + cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + + utils::checkEqual(sum.to_matrix(dimensions), sum_expected); + utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); + utils::checkEqual(difference.to_matrix(dimensions), diff_expected); + utils::checkEqual(difference_reverse.to_matrix(dimensions), + diff_reverse_expected); } diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index fb0a1f04ff..5a65a1e5ea 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -6,10 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "utils.h" #include "cudaq/operators.h" +#include "utils.h" #include -#include "cudaq/dynamics/spin_operators.h" #include @@ -21,241 +20,246 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; + {// Same degrees of freedom. + {auto spin0 = cudaq::spin_operator::x(5); + auto spin1 = cudaq::spin_operator::z(5); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {5}; + auto spin_matrix = utils::PauliX_matrix() * utils::PauliZ_matrix(); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(5); + auto op1 = cudaq::matrix_operator::create(5); + + auto got = op0 * op1; + utils::assert_product_equal(got, 1., + {op0.get_terms()[0], op1.get_terms()[0]}); + ASSERT_TRUE(got.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{5, level_count}}); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + auto want_matrix = matrix0 * matrix1; + utils::checkEqual(want_matrix, got_matrix); + } +} + +// Different degrees of freedom. +{ + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(1); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {1, 0}; + auto spin_matrix = + cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(1); + + cudaq::product_operator got = op0 * op1; + cudaq::product_operator got_reverse = op1 * op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + +// Different degrees of freedom, non-consecutive. +// Should produce the same matrices as the above test. +{ + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = + cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); + + cudaq::product_operator got = op0 * op1; + cudaq::product_operator got_reverse = op1 * op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0, level_count}, {2, level_count}}); + auto got_matrix_reverse = + got_reverse.to_matrix({{0, level_count}, {2, level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + +// Different degrees of freedom, non-consecutive but all dimensions +// provided. +{ + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = + cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::create(2); + + cudaq::product_operator got = op0 * op1; + cudaq::product_operator got_reverse = op1 * op0; + + std::vector want_degrees = {2, 0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + dimensions = {{0, level_count}, {1, level_count}, {2, level_count}}; + auto got_matrix = got.to_matrix(dimensions); + auto got_matrix_reverse = got_reverse.to_matrix(dimensions); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::annihilate_matrix(level_count); + auto matrix1 = utils::create_matrix(level_count); + + std::vector matrices_0; + std::vector matrices_1; + matrices_0 = {identity, matrix0}; + matrices_1 = {matrix1, identity}; + + auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(got_matrix, want_matrix); + } +} +} + +// Scalar Ops against Elementary Ops +{ + auto function = [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; + + // matrix operator against constant { - // Same degrees of freedom. - { - auto spin0 = cudaq::spin_operator::x(5); - auto spin1 = cudaq::spin_operator::z(5); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {5}; - auto spin_matrix = utils::PauliX_matrix() * utils::PauliZ_matrix(); - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(5); - auto op1 = cudaq::matrix_operator::create(5); - - auto got = op0 * op1; - utils::assert_product_equal(got, 1., {op0.get_terms()[0], op1.get_terms()[0]}); - ASSERT_TRUE(got.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{5, level_count}}); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); - auto want_matrix = matrix0 * matrix1; - utils::checkEqual(want_matrix, got_matrix); - } - } - - // Different degrees of freedom. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(1); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {1, 0}; - auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(1); - - cudaq::product_operator got = op0 * op1; - cudaq::product_operator got_reverse = op1 * op0; - - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = - got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); - - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 * fullHilbert1; - auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - } - - // Different degrees of freedom, non-consecutive. - // Should produce the same matrices as the above test. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); - - cudaq::product_operator got = op0 * op1; - cudaq::product_operator got_reverse = op1 * op0; - - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{0, level_count}, {2, level_count}}); - auto got_matrix_reverse = - got_reverse.to_matrix({{0, level_count}, {2, level_count}}); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); - - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 * fullHilbert1; - auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - } - - // Different degrees of freedom, non-consecutive but all dimensions - // provided. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); - - cudaq::product_operator got = op0 * op1; - cudaq::product_operator got_reverse = op1 * op0; - - std::vector want_degrees = {2, 0}; - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - dimensions = {{0, level_count},{1, level_count},{2, level_count}}; - auto got_matrix = got.to_matrix(dimensions); - auto got_matrix_reverse = got_reverse.to_matrix(dimensions); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); - - std::vector matrices_0; - std::vector matrices_1; - matrices_0 = {identity, matrix0}; - matrices_1 = {matrix1, identity}; - - auto fullHilbert0 = - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - auto fullHilbert1 = - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto want_matrix = fullHilbert0 * fullHilbert1; - auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(got_matrix, want_matrix); - } - } + auto op = cudaq::matrix_operator::annihilate(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::annihilate_matrix(2); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); } - // Scalar Ops against Elementary Ops + // spin operator against constant { - auto function = [](const std::unordered_map> ¶meters) { - auto entry = parameters.find("value"); - if (entry == parameters.end()) - throw std::runtime_error("value not defined in parameters"); - return entry->second; - }; - - // matrix operator against constant - { - auto op = cudaq::matrix_operator::annihilate(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::annihilate_matrix(2); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); - utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); - } - - // spin operator against constant - { - auto op = cudaq::spin_operator::x(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::PauliX_matrix(); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(value_0 * op_matrix, product.to_matrix()); - utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); - } - - // matrix operator against constant from lambda - { - auto op = cudaq::matrix_operator::annihilate(1); - auto scalar_op = cudaq::scalar_operator(function); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::annihilate_matrix(2); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({{1, 2}}, {{"value", 0.3}})); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); - } - - // spin operator against constant from lambda - { - auto op = cudaq::spin_operator::x(1); - auto scalar_op = cudaq::scalar_operator(function); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::PauliX_matrix(); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({}, {{"value", 0.3}})); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({}, {{"value", 0.3}})); - } + auto op = cudaq::spin_operator::x(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix()); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); + } + + // matrix operator against constant from lambda + { + auto op = cudaq::matrix_operator::annihilate(1); + auto scalar_op = cudaq::scalar_operator(function); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::annihilate_matrix(2); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, + product.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, + reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); + } + + // spin operator against constant from lambda + { + auto op = cudaq::spin_operator::x(1); + auto scalar_op = cudaq::scalar_operator(function); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, + product.to_matrix({}, {{"value", 0.3}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, + reverse.to_matrix({}, {{"value", 0.3}})); } } +} TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::complex value_0 = 0.1 + 0.1; @@ -276,8 +280,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(sum.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = sum.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); @@ -317,7 +322,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; - auto scaled_identity = value_0 * utils::id_matrix(level_count * level_count); + auto scaled_identity = + value_0 * utils::id_matrix(level_count * level_count); auto want_matrix = scaled_identity + product; auto want_matrix_reverse = product + scaled_identity; @@ -328,8 +334,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `spin product + complex` { - auto product_op = cudaq::spin_operator::x(0) * - cudaq::spin_operator::y(1); + auto product_op = cudaq::spin_operator::x(0) * cudaq::spin_operator::y(1); auto sum = value_0 + product_op; auto reverse = product_op + value_0; @@ -344,10 +349,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = sum.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); - auto term_0 = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto term_1 = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + auto term_0 = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto term_1 = cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto product = term_0 * term_1; auto scaled_identity = value_0 * utils::id_matrix(2 * 2); @@ -408,8 +411,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { ASSERT_TRUE(difference.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = difference.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = + difference.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); @@ -427,8 +432,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `spin product - double` { - auto product_op = cudaq::spin_operator::i(0) * - cudaq::spin_operator::z(1); + auto product_op = cudaq::spin_operator::i(0) * cudaq::spin_operator::z(1); auto sum = 2.0 - product_op; auto reverse = product_op - 2.0; @@ -443,10 +447,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = sum.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); - auto term_0 = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(2)); - auto term_1 = cudaq::kronecker(utils::PauliZ_matrix(), - utils::id_matrix(2)); + auto term_0 = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); + auto term_1 = cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); auto product = term_0 * term_1; auto scaled_identity = 2.0 * utils::id_matrix(2 * 2); @@ -530,25 +532,29 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * double` { - auto product_op = cudaq::matrix_operator::parity(0) * - cudaq::matrix_operator::parity(1); + auto product_op = + cudaq::matrix_operator::parity(0) * cudaq::matrix_operator::parity(1); ASSERT_TRUE(product_op.num_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == + std::complex(1.)); auto product = 2.0 * product_op; auto reverse = product_op * 2.0; ASSERT_TRUE(product.num_terms() == 2); ASSERT_TRUE(reverse.num_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); - ASSERT_TRUE(reverse.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); + ASSERT_TRUE(reverse.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); - auto got_matrix_reverse = reverse.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = + reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), utils::parity_matrix(level_count)); @@ -566,10 +572,11 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator * complex` { - auto product_op = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::number(1); + auto product_op = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); ASSERT_TRUE(product_op.num_terms() == 2); - ASSERT_TRUE(product_op.get_coefficient().evaluate() == std::complex(1.)); + ASSERT_TRUE(product_op.get_coefficient().evaluate() == + std::complex(1.)); auto product = value_0 * product_op; auto reverse = product_op * value_0; @@ -641,8 +648,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `spin product * scalar_operator` { - auto product_op = cudaq::spin_operator::z(0) * - cudaq::spin_operator::y(1); + auto product_op = cudaq::spin_operator::z(0) * cudaq::spin_operator::y(1); auto scalar_op = cudaq::scalar_operator(value_0); auto product = scalar_op * product_op; @@ -660,13 +666,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = product.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); - auto term_0 = cudaq::kronecker(utils::id_matrix(2), - utils::PauliZ_matrix()); - auto term_1 = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + auto term_0 = cudaq::kronecker(utils::id_matrix(2), utils::PauliZ_matrix()); + auto term_1 = cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto product_matrix = term_0 * term_1; - auto scaled_identity = - value_0 * utils::id_matrix(2 * 2); + auto scaled_identity = value_0 * utils::id_matrix(2 * 2); auto want_matrix = scaled_identity * product_matrix; auto want_matrix_reverse = product_matrix * scaled_identity; @@ -682,7 +685,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { product *= 2.0; ASSERT_TRUE(product.num_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); @@ -703,22 +707,20 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `spin product *= double` { - auto product = cudaq::spin_operator::y(0) * - cudaq::spin_operator::i(1); + auto product = cudaq::spin_operator::y(0) * cudaq::spin_operator::i(1); product *= 2.0; ASSERT_TRUE(product.num_terms() == 2); - ASSERT_TRUE(product.get_coefficient().evaluate() == std::complex(2.)); + ASSERT_TRUE(product.get_coefficient().evaluate() == + std::complex(2.)); std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); auto got_matrix = product.to_matrix(); - auto term_0 = cudaq::kronecker(utils::id_matrix(2), - utils::PauliY_matrix()); - auto term_1 = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(2)); + auto term_0 = cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); + auto term_1 = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); auto product_matrix = term_0 * term_1; auto scaled_identity = 2.0 * utils::id_matrix(2 * 2); @@ -729,8 +731,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= complex` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::momentum(1); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::momentum(1); product *= value_0; ASSERT_TRUE(product.num_terms() == 2); @@ -739,7 +741,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto got_matrix = product.to_matrix({{0,level_count},{1,level_count}}); + auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), utils::number_matrix(level_count)); @@ -787,7 +789,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { int level_count = 3; - std::unordered_map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::unordered_map dimensions = { + {0, level_count}, {1, level_count}, {2, level_count + 1}}; // `product_operator + product_operator` { @@ -836,10 +839,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `spin product + spin product` { - auto term_0 = cudaq::spin_operator::z(0) * - cudaq::spin_operator::y(2); - auto term_1 = cudaq::spin_operator::x(2) * - cudaq::spin_operator::z(4); + auto term_0 = cudaq::spin_operator::z(0) * cudaq::spin_operator::y(2); + auto term_1 = cudaq::spin_operator::x(2) * cudaq::spin_operator::z(4); auto sum = term_0 + term_1; @@ -852,20 +853,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils::id_matrix(2), - utils::id_matrix(2), + matrices_0_0 = {utils::id_matrix(2), utils::id_matrix(2), utils::PauliZ_matrix()}; - matrices_0_1 = {utils::id_matrix(2), - utils::PauliY_matrix(), + matrices_0_1 = {utils::id_matrix(2), utils::PauliY_matrix(), utils::id_matrix(2)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils::id_matrix(2), - utils::PauliX_matrix(), + matrices_1_0 = {utils::id_matrix(2), utils::PauliX_matrix(), utils::id_matrix(2)}; - matrices_1_1 = {utils::PauliZ_matrix(), - utils::id_matrix(2), + matrices_1_1 = {utils::PauliZ_matrix(), utils::id_matrix(2), utils::id_matrix(2)}; auto term_0_matrix = @@ -883,8 +880,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::annihilate(0) * cudaq::matrix_operator::number(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::momentum(2); + auto term_1 = + cudaq::matrix_operator::create(1) * cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -924,8 +921,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `spin product - spin product` { auto term_0 = cudaq::spin_operator::i(0); - auto term_1 = cudaq::spin_operator::x(1) * - cudaq::spin_operator::y(2); + auto term_1 = cudaq::spin_operator::x(1) * cudaq::spin_operator::y(2); auto difference = term_0 - term_1; auto reverse = term_1 - term_0; @@ -937,17 +933,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { auto reverse_matrix = reverse.to_matrix(); std::vector matrices_0_0; - matrices_0_0 = {utils::id_matrix(2), - utils::id_matrix(2), + matrices_0_0 = {utils::id_matrix(2), utils::id_matrix(2), utils::id_matrix(2)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils::id_matrix(2), - utils::PauliX_matrix(), + matrices_1_0 = {utils::id_matrix(2), utils::PauliX_matrix(), utils::id_matrix(2)}; - matrices_1_1 = {utils::PauliY_matrix(), - utils::id_matrix(2), + matrices_1_1 = {utils::PauliY_matrix(), utils::id_matrix(2), utils::id_matrix(2)}; auto term_0_matrix = @@ -966,8 +959,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::annihilate(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::parity(2); + auto term_1 = + cudaq::matrix_operator::create(1) * cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -1006,10 +999,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `spin product * spin product` { - auto term_0 = cudaq::spin_operator::y(0) * - cudaq::spin_operator::x(1); - auto term_1 = cudaq::spin_operator::z(1) * - cudaq::spin_operator::i(3); + auto term_0 = cudaq::spin_operator::y(0) * cudaq::spin_operator::x(1); + auto term_1 = cudaq::spin_operator::z(1) * cudaq::spin_operator::i(3); auto product = term_0 * term_1; auto reverse = term_1 * term_0; @@ -1024,20 +1015,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils::id_matrix(2), - utils::id_matrix(2), + matrices_0_0 = {utils::id_matrix(2), utils::id_matrix(2), utils::PauliY_matrix()}; - matrices_0_1 = {utils::id_matrix(2), - utils::PauliX_matrix(), + matrices_0_1 = {utils::id_matrix(2), utils::PauliX_matrix(), utils::id_matrix(2)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils::id_matrix(2), - utils::PauliZ_matrix(), + matrices_1_0 = {utils::id_matrix(2), utils::PauliZ_matrix(), utils::id_matrix(2)}; - matrices_1_1 = {utils::id_matrix(2), - utils::id_matrix(2), + matrices_1_1 = {utils::id_matrix(2), utils::id_matrix(2), utils::id_matrix(2)}; auto term_0_matrix = @@ -1092,17 +1079,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - auto term1_only_matrix = cudaq::kronecker(utils::annihilate_matrix(level_count + 1), utils::create_matrix(level_count)); + auto term1_only_matrix = + cudaq::kronecker(utils::annihilate_matrix(level_count + 1), + utils::create_matrix(level_count)); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(term1_only_matrix, term_1.to_matrix(dimensions)); } // `spin product *= spin product` { - auto term_0 = cudaq::spin_operator::y(3) * - cudaq::spin_operator::y(1); - auto term_1 = cudaq::spin_operator::z(1) * - cudaq::spin_operator::x(0); + auto term_0 = cudaq::spin_operator::y(3) * cudaq::spin_operator::y(1); + auto term_1 = cudaq::spin_operator::z(1) * cudaq::spin_operator::x(0); term_0 *= term_1; @@ -1112,20 +1099,16 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_0; std::vector matrices_0_1; - matrices_0_0 = {utils::PauliY_matrix(), - utils::id_matrix(2), + matrices_0_0 = {utils::PauliY_matrix(), utils::id_matrix(2), utils::id_matrix(2)}; - matrices_0_1 = {utils::id_matrix(2), - utils::PauliY_matrix(), + matrices_0_1 = {utils::id_matrix(2), utils::PauliY_matrix(), utils::id_matrix(2)}; std::vector matrices_1_0; std::vector matrices_1_1; - matrices_1_0 = {utils::id_matrix(2), - utils::PauliZ_matrix(), + matrices_1_0 = {utils::id_matrix(2), utils::PauliZ_matrix(), utils::id_matrix(2)}; - matrices_1_1 = {utils::id_matrix(2), - utils::id_matrix(2), + matrices_1_1 = {utils::id_matrix(2), utils::id_matrix(2), utils::PauliX_matrix()}; auto term_0_matrix = @@ -1136,7 +1119,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - auto term1_only_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + auto term1_only_matrix = + cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(term1_only_matrix, term_1.to_matrix()); } @@ -1145,7 +1129,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { int level_count = 3; - std::unordered_map dimensions = {{0,level_count}, {1,level_count}, {2,level_count+1}}; + std::unordered_map dimensions = { + {0, level_count}, {1, level_count}, {2, level_count + 1}}; // `product_operator + operator_sum` { @@ -1168,14 +1153,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)}; + utils::annihilate_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::create_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count), utils::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1192,10 +1176,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `spin product + spin sum` { - auto product = cudaq::spin_operator::x(0) * - cudaq::spin_operator::y(1); - auto original_sum = cudaq::spin_operator::z(1) + - cudaq::spin_operator::i(2); + auto product = cudaq::spin_operator::x(0) * cudaq::spin_operator::y(1); + auto original_sum = cudaq::spin_operator::z(1) + cudaq::spin_operator::i(2); auto sum = product + original_sum; auto reverse = original_sum + product; @@ -1249,14 +1231,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)}; + utils::annihilate_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::create_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count), utils::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1273,10 +1254,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `spin product - spin sum` { - auto product = cudaq::spin_operator::y(0) * - cudaq::spin_operator::z(1); - auto original_difference = cudaq::spin_operator::x(1) - - cudaq::spin_operator::i(2); + auto product = cudaq::spin_operator::y(0) * cudaq::spin_operator::z(1); + auto original_difference = + cudaq::spin_operator::x(1) - cudaq::spin_operator::i(2); auto difference = product - original_difference; auto reverse = original_difference - product; @@ -1284,7 +1264,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(difference.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto got_matrix = difference.to_matrix(); + auto got_matrix = difference.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); std::vector matrices_0_0 = { @@ -1330,14 +1310,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::annihilate_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), - utils::id_matrix(level_count)}; + utils::annihilate_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::create_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 1), - utils::id_matrix(level_count), utils::id_matrix(level_count)}; + utils::create_matrix(level_count + 1), utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1354,10 +1333,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `spin product * spin sum` { - auto original_product = cudaq::spin_operator::z(0) * - cudaq::spin_operator::y(1); - auto sum = cudaq::spin_operator::i(1) + - cudaq::spin_operator::x(2); + auto original_product = + cudaq::spin_operator::z(0) * cudaq::spin_operator::y(1); + auto sum = cudaq::spin_operator::i(1) + cudaq::spin_operator::x(2); auto product = original_product * sum; auto reverse = sum * original_product; @@ -1365,7 +1343,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { ASSERT_TRUE(product.num_terms() == 2); ASSERT_TRUE(reverse.num_terms() == 2); - auto got_matrix = product.to_matrix(); + auto got_matrix = product.to_matrix(); auto got_matrix_reverse = reverse.to_matrix(); std::vector matrices_0_0 = { @@ -1392,62 +1370,72 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { } TEST(OperatorExpressions, checkCustomProductOps) { - auto level_count = 2; - std::unordered_map dimensions = {{0, level_count + 1}, {1, level_count + 2}, {2, level_count}, {3, level_count + 3}}; - - { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } - - auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); - auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); - auto product = op0 * op1; - auto reverse = op1 * op0; - - std::vector matrices = { + auto level_count = 2; + std::unordered_map dimensions = {{0, level_count + 1}, + {1, level_count + 2}, + {2, level_count}, + {3, level_count + 3}}; + + { + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::create_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } + + auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); + auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); + auto product = op0 * op1; + auto reverse = op1 * op0; + + std::vector matrices = { utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * utils::create_matrix(level_count + 2), + utils::position_matrix(level_count + 2) * + utils::create_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; - auto expected = cudaq::kronecker(matrices.begin(), matrices.end()); + auto expected = cudaq::kronecker(matrices.begin(), matrices.end()); - std::vector matrices_reverse = { + std::vector matrices_reverse = { utils::number_matrix(level_count), - utils::create_matrix(level_count + 2) * utils::position_matrix(level_count + 2), + utils::create_matrix(level_count + 2) * + utils::position_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; - auto expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); - - utils::checkEqual(product.to_matrix(dimensions), expected); - utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); - - op0 = cudaq::matrix_operator::instantiate("custom_op0", {2, 3}); - op1 = cudaq::matrix_operator::instantiate("custom_op1", {2, 0}); - product = op0 * op1; - reverse = op1 * op0; - - matrices = { - utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count) * utils::create_matrix(level_count), - utils::number_matrix(level_count + 1)}; - expected = cudaq::kronecker(matrices.begin(), matrices.end()); - - matrices_reverse = { - utils::position_matrix(level_count + 3), - utils::create_matrix(level_count) * utils::momentum_matrix(level_count), - utils::number_matrix(level_count + 1)}; - expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); - - utils::checkEqual(product.to_matrix(dimensions), expected); - utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); + auto expected_reverse = + cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); + + utils::checkEqual(product.to_matrix(dimensions), expected); + utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); + + op0 = cudaq::matrix_operator::instantiate("custom_op0", {2, 3}); + op1 = cudaq::matrix_operator::instantiate("custom_op1", {2, 0}); + product = op0 * op1; + reverse = op1 * op0; + + matrices = {utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count) * + utils::create_matrix(level_count), + utils::number_matrix(level_count + 1)}; + expected = cudaq::kronecker(matrices.begin(), matrices.end()); + + matrices_reverse = {utils::position_matrix(level_count + 3), + utils::create_matrix(level_count) * + utils::momentum_matrix(level_count), + utils::number_matrix(level_count + 1)}; + expected_reverse = + cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); + + utils::checkEqual(product.to_matrix(dimensions), expected); + utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); } - diff --git a/unittests/dynamics/scalar_operator.cpp b/unittests/dynamics/scalar_operator.cpp index 77a3c53cdb..8332a1dc22 100644 --- a/unittests/dynamics/scalar_operator.cpp +++ b/unittests/dynamics/scalar_operator.cpp @@ -9,9 +9,7 @@ #include "cudaq/operators.h" #include -cudaq::scalar_operator negate(cudaq::scalar_operator op) { - return -1.0 * op; -} +cudaq::scalar_operator negate(cudaq::scalar_operator op) { return -1.0 * op; } TEST(OperatorExpressions, checkScalarOpsUnary) { auto scalar = cudaq::scalar_operator(1.0); @@ -46,12 +44,14 @@ TEST(OperatorExpressions, checkScalarOpsSimpleComplex) { // From a lambda function. { - auto function = [](const std::unordered_map> ¶meters) { - auto entry = parameters.find("value"); - if (entry == parameters.end()) - throw std::runtime_error("value not defined in parameters"); - return entry->second; - }; + auto function = + [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; std::unordered_map> parameter_map; @@ -103,12 +103,14 @@ TEST(OperatorExpressions, checkScalarOpsSimpleDouble) { // From a lambda function. { - auto function = [](const std::unordered_map> ¶meters) { - auto entry = parameters.find("value"); - if (entry == parameters.end()) - throw std::runtime_error("value not defined in parameters"); - return entry->second; - }; + auto function = + [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; std::unordered_map> parameter_map; @@ -140,7 +142,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticComplex) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - auto function = [](const std::unordered_map> ¶meters) { + auto function = [](const std::unordered_map> + ¶meters) { auto entry = parameters.find("value"); if (entry == parameters.end()) throw std::runtime_error("value not defined in parameters"); @@ -381,7 +384,8 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { std::complex value_2 = 2.0 + 0.1; std::complex value_3 = 2.0 + 1.0; - auto function = [](const std::unordered_map> ¶meters) { + auto function = [](const std::unordered_map> + ¶meters) { auto entry = parameters.find("value"); if (entry == parameters.end()) throw std::runtime_error("value not defined in parameters"); @@ -391,12 +395,14 @@ TEST(OperatorExpressions, checkScalarOpsArithmeticScalarOps) { // I use another function here to make sure that local variables // that may be unique to each ScalarOp's generators are both kept // track of when we merge the generators. - auto alternative_function = [](const std::unordered_map> ¶meters) { - auto entry = parameters.find("other"); - if (entry == parameters.end()) - throw std::runtime_error("other not defined in parameters"); - return entry->second; - }; + auto alternative_function = + [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("other"); + if (entry == parameters.end()) + throw std::runtime_error("other not defined in parameters"); + return entry->second; + }; // + : Constant scalar operator. { diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp index 1a811785f4..835e6e5cce 100644 --- a/unittests/dynamics/spin_operator.cpp +++ b/unittests/dynamics/spin_operator.cpp @@ -6,10 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "utils.h" #include "cudaq/operators.h" +#include "utils.h" #include -#include "cudaq/dynamics/spin_operators.h" TEST(OperatorExpressions, checkSpinOpsUnary) { auto op = cudaq::spin_operator::x(0); @@ -121,7 +120,8 @@ TEST(OperatorExpressions, checkSpinOpsWithComplex) { TEST(OperatorExpressions, checkSpinOpsWithScalars) { - auto function = [](const std::unordered_map> ¶meters) { + auto function = [](const std::unordered_map> + ¶meters) { auto entry = parameters.find("value"); if (entry == parameters.end()) throw std::runtime_error("value not defined in parameters"); @@ -165,7 +165,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = sum.to_matrix({}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = + reverse.to_matrix({}, {{"value", const_scale_factor}}); auto want_matrix = utils::PauliY_matrix() + scaled_identity; auto want_reverse_matrix = scaled_identity + utils::PauliY_matrix(); utils::checkEqual(want_matrix, got_matrix); @@ -205,7 +206,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = sum.to_matrix({}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = + reverse.to_matrix({}, {{"value", const_scale_factor}}); auto want_matrix = utils::PauliZ_matrix() - scaled_identity; auto want_reverse_matrix = scaled_identity - utils::PauliZ_matrix(); utils::checkEqual(want_matrix, got_matrix); @@ -247,7 +249,8 @@ TEST(OperatorExpressions, checkSpinOpsWithScalars) { auto scaled_identity = const_scale_factor * utils::id_matrix(2); auto got_matrix = product.to_matrix({}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = + reverse.to_matrix({}, {{"value", const_scale_factor}}); auto want_matrix = utils::PauliZ_matrix() * scaled_identity; auto want_reverse_matrix = scaled_identity * utils::PauliZ_matrix(); utils::checkEqual(want_matrix, got_matrix); @@ -266,8 +269,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(); - auto want_matrix = utils::PauliX_matrix() + - utils::PauliY_matrix(); + auto want_matrix = utils::PauliX_matrix() + utils::PauliY_matrix(); utils::checkEqual(want_matrix, got_matrix); } @@ -279,10 +281,10 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { auto sum = self + other; ASSERT_TRUE(sum.num_terms() == 2); - auto matrix_self = cudaq::kronecker(utils::id_matrix(2), - utils::PauliZ_matrix()); - auto matrix_other = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + auto matrix_self = + cudaq::kronecker(utils::id_matrix(2), utils::PauliZ_matrix()); + auto matrix_other = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto got_matrix = sum.to_matrix(); auto want_matrix = matrix_self + matrix_other; utils::checkEqual(want_matrix, got_matrix); @@ -297,8 +299,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(); - auto want_matrix = utils::PauliZ_matrix() - - utils::PauliX_matrix(); + auto want_matrix = utils::PauliZ_matrix() - utils::PauliX_matrix(); utils::checkEqual(want_matrix, got_matrix); } @@ -311,10 +312,9 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { ASSERT_TRUE(sum.num_terms() == 2); auto annihilate_full = - cudaq::kronecker(utils::id_matrix(2), - utils::PauliY_matrix()); - auto create_full = cudaq::kronecker(utils::PauliX_matrix(), - utils::id_matrix(2)); + cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); + auto create_full = + cudaq::kronecker(utils::PauliX_matrix(), utils::id_matrix(2)); auto got_matrix = sum.to_matrix(); auto want_matrix = annihilate_full - create_full; utils::checkEqual(want_matrix, got_matrix); @@ -332,8 +332,7 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { ASSERT_TRUE(product.degrees() == want_degrees); auto got_matrix = product.to_matrix(); - auto want_matrix = utils::PauliY_matrix() * - utils::PauliZ_matrix(); + auto want_matrix = utils::PauliY_matrix() * utils::PauliZ_matrix(); utils::checkEqual(want_matrix, got_matrix); } @@ -349,10 +348,9 @@ TEST(OperatorExpressions, checkSpinOpsSimpleArithmetics) { ASSERT_TRUE(product.degrees() == want_degrees); auto annihilate_full = - cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto create_full = cudaq::kronecker(utils::PauliZ_matrix(), - utils::id_matrix(2)); + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto create_full = + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); auto got_matrix = product.to_matrix(); auto want_matrix = annihilate_full * create_full; utils::checkEqual(want_matrix, got_matrix); @@ -367,8 +365,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { // `spin_operator + operator_sum` { auto self = cudaq::spin_operator::y(2); - auto operator_sum = cudaq::spin_operator::y(2) + - cudaq::spin_operator::x(1); + auto operator_sum = cudaq::spin_operator::y(2) + cudaq::spin_operator::x(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -376,12 +373,12 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { ASSERT_TRUE(got.num_terms() == 2); ASSERT_TRUE(reverse.num_terms() == 2); - auto self_full = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); - auto term_0_full = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); - auto term_1_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); + auto self_full = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); + auto term_0_full = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); + auto term_1_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); auto got_matrix = got.to_matrix(); auto got_reverse_matrix = reverse.to_matrix(); @@ -394,8 +391,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { // `spin_operator - operator_sum` { auto self = cudaq::spin_operator::i(0); - auto operator_sum = cudaq::spin_operator::x(0) + - cudaq::spin_operator::z(1); + auto operator_sum = cudaq::spin_operator::x(0) + cudaq::spin_operator::z(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -403,14 +399,13 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { ASSERT_TRUE(got.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto self_full = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(2)); - auto term_0_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto term_1_full = cudaq::kronecker(utils::PauliZ_matrix(), - utils::id_matrix(2)); + auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto term_1_full = + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - auto got_matrix = got.to_matrix(); + auto got_matrix = got.to_matrix(); auto got_reverse_matrix = reverse.to_matrix(); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; @@ -421,8 +416,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { // `spin_operator * operator_sum` { auto self = cudaq::spin_operator::y(0); - auto operator_sum = cudaq::spin_operator::x(0) + - cudaq::spin_operator::y(2); + auto operator_sum = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(2); auto got = self * operator_sum; auto reverse = operator_sum * self; @@ -434,16 +428,15 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { for (auto &term : reverse.get_terms()) ASSERT_TRUE(term.num_terms() == term.degrees().size()); - auto self_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliY_matrix()); + auto self_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); auto term_0_full = - cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto term_1_full = cudaq::kronecker(utils::PauliY_matrix(), - utils::id_matrix(2)); + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto term_1_full = + cudaq::kronecker(utils::PauliY_matrix(), utils::id_matrix(2)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = got.to_matrix(); + auto got_matrix = got.to_matrix(); auto got_reverse_matrix = reverse.to_matrix(); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; @@ -453,19 +446,17 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { // `operator_sum += spin_operator` { - auto operator_sum = cudaq::spin_operator::z(0) + - cudaq::spin_operator::x(2); + auto operator_sum = cudaq::spin_operator::z(0) + cudaq::spin_operator::x(2); operator_sum += cudaq::spin_operator::y(0); ASSERT_TRUE(operator_sum.num_terms() == 3); auto self_full = - cudaq::kronecker(utils::id_matrix(2), - utils::PauliZ_matrix()); - auto term_0_full = cudaq::kronecker(utils::PauliX_matrix(), - utils::id_matrix(2)); - auto term_1_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliY_matrix()); + cudaq::kronecker(utils::id_matrix(2), utils::PauliZ_matrix()); + auto term_0_full = + cudaq::kronecker(utils::PauliX_matrix(), utils::id_matrix(2)); + auto term_1_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); auto got_matrix = operator_sum.to_matrix(); auto want_matrix = term_0_full + term_1_full + self_full; @@ -474,18 +465,17 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { // `operator_sum -= spin_operator` { - auto operator_sum = cudaq::spin_operator::x(0) + - cudaq::spin_operator::i(1); + auto operator_sum = cudaq::spin_operator::x(0) + cudaq::spin_operator::i(1); operator_sum -= cudaq::spin_operator::x(0); ASSERT_TRUE(operator_sum.num_terms() == 2); - auto self_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); - auto term_0_full = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(2)); - auto term_1_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliX_matrix()); + auto self_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); + auto term_1_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()); auto got_matrix = operator_sum.to_matrix(); auto want_matrix = term_0_full + term_1_full - self_full; @@ -495,8 +485,7 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { // `operator_sum *= spin_operator` { auto self = cudaq::spin_operator::i(0); - auto operator_sum = cudaq::spin_operator::y(0) + - cudaq::spin_operator::z(1); + auto operator_sum = cudaq::spin_operator::y(0) + cudaq::spin_operator::z(1); operator_sum *= self; @@ -504,12 +493,11 @@ TEST(OperatorExpressions, checkSpinOpsAdvancedArithmetics) { for (auto &term : operator_sum.get_terms()) ASSERT_TRUE(term.num_terms() == term.degrees().size()); - auto self_full = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(2)); - auto term_0_full = cudaq::kronecker(utils::id_matrix(2), - utils::PauliY_matrix()); - auto term_1_full = cudaq::kronecker(utils::PauliZ_matrix(), - utils::id_matrix(2)); + auto self_full = cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(2)); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(2), utils::PauliY_matrix()); + auto term_1_full = + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); auto sum_full = term_0_full + term_1_full; auto got_matrix = operator_sum.to_matrix(); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 9e2015a13d..15f6fd46ce 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -7,10 +7,10 @@ ******************************************************************************/ #include "test_mocks.h" +#include #include #include #include -#include #include // Initialize operator_sum @@ -124,11 +124,13 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { std::vector mode_extents = {2, 2}; - auto callback_function = [](std::map>) { - return std::complex(1.5, 0.0); - }; + auto callback_function = + [](std::unordered_map>) { + return std::complex(1.5, 0.0); + }; - cudaq::scalar_operator scalar_callback(callback_function); + cudaq::ScalarCallbackFunction scalar_callback_function(callback_function); + cudaq::scalar_operator scalar_callback(scalar_callback_function); auto op_sum = scalar_callback * cudaq::matrix_operator::create(0); @@ -147,7 +149,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { const std::string op_id = "custom_op"; auto func = [](std::vector dimensions, - std::map> _none) { + std::unordered_map> _none) { if (dimensions.size() != 1) throw std::invalid_argument("Must have a singe dimension"); if (dimensions[0] != 2) @@ -158,7 +160,8 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { return mat; }; cudaq::matrix_operator::define(op_id, {-1}, func); - cudaq::matrix_operator matrix_op(op_id, {0}); + auto matrix_op = + cudaq::matrix_operator::instantiate(op_id, {0}).get_terms()[0]; auto wrapped_tensor_callback = cudaq::cudm_helper::_wrap_tensor_callback(matrix_op); diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 28c12f36fa..62548c0933 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -17,39 +17,16 @@ TEST(EvolveTester, checkSimple) { const std::map dims = {{0, 2}}; - const std::string op_id = "pauli_x"; - auto func = [](std::vector dimensions, - std::map> _none) { - if (dimensions.size() != 1) - throw std::invalid_argument("Must have a singe dimension"); - if (dimensions[0] != 2) - throw std::invalid_argument("Must have dimension 2"); - auto mat = cudaq::matrix_2(2, 2); - mat[{1, 0}] = 1.0; - mat[{0, 1}] = 1.0; - return mat; - }; - cudaq::matrix_operator::define(op_id, {-1}, func); - auto ham = cudaq::product_operator( - 2.0 * M_PI * 0.1, cudaq::matrix_operator(op_id, {0})); + cudaq::product_operator ham1 = + (2.0 * M_PI * 0.1 * cudaq::spin_operator::x(0)); + cudaq::operator_sum ham(ham1); + constexpr int numSteps = 10; cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); - cudaq::matrix_operator::define( - "pauli_z", {-1}, - [](std::vector dimensions, - std::map> _none) { - if (dimensions.size() != 1) - throw std::invalid_argument("Must have a singe dimension"); - if (dimensions[0] != 2) - throw std::invalid_argument("Must have dimension 2"); - auto mat = cudaq::matrix_2(2, 2); - mat[{0, 0}] = 1.0; - mat[{1, 1}] = -1.0; - return mat; - }); - auto pauliZ = cudaq::product_operator( - std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); + cudaq::product_operator pauliZ_t = + cudaq::spin_operator::z(0); + cudaq::operator_sum pauliZ(pauliZ_t); auto initialState = cudaq::state::from_data(std::vector>{1.0, 0.0}); @@ -75,39 +52,16 @@ TEST(EvolveTester, checkSimple) { TEST(EvolveTester, checkSimpleDensityMatrix) { const std::map dims = {{0, 2}}; - const std::string op_id = "pauli_x"; - auto func = [](std::vector dimensions, - std::map> _none) { - if (dimensions.size() != 1) - throw std::invalid_argument("Must have a singe dimension"); - if (dimensions[0] != 2) - throw std::invalid_argument("Must have dimension 2"); - auto mat = cudaq::matrix_2(2, 2); - mat[{1, 0}] = 1.0; - mat[{0, 1}] = 1.0; - return mat; - }; - cudaq::matrix_operator::define(op_id, {-1}, func); - auto ham = cudaq::product_operator( - 2.0 * M_PI * 0.1, cudaq::matrix_operator(op_id, {0})); + cudaq::product_operator ham1 = + (2.0 * M_PI * 0.1 * cudaq::spin_operator::x(0)); + cudaq::operator_sum ham(ham1); + constexpr int numSteps = 10; cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); - cudaq::matrix_operator::define( - "pauli_z", {-1}, - [](std::vector dimensions, - std::map> _none) { - if (dimensions.size() != 1) - throw std::invalid_argument("Must have a singe dimension"); - if (dimensions[0] != 2) - throw std::invalid_argument("Must have dimension 2"); - auto mat = cudaq::matrix_2(2, 2); - mat[{0, 0}] = 1.0; - mat[{1, 1}] = -1.0; - return mat; - }); - auto pauliZ = cudaq::product_operator( - std::complex{1.0, 0.0}, cudaq::matrix_operator("pauli_z", {0})); + cudaq::product_operator pauliZ_t = + cudaq::spin_operator::z(0); + cudaq::operator_sum pauliZ(pauliZ_t); auto initialState = cudaq::state::from_data( std::vector>{1.0, 0.0, 0.0, 0.0}); @@ -140,8 +94,14 @@ TEST(EvolveTester, checkCompositeSystem) { auto sm = cudaq::matrix_operator::annihilate(0); auto sm_dag = cudaq::matrix_operator::create(0); - auto atom_occ_op = cudaq::matrix_operator::number(0); - auto cavity_occ_op = cudaq::matrix_operator::number(1); + cudaq::product_operator atom_occ_op_t = + cudaq::matrix_operator::number(0); + cudaq::operator_sum atom_occ_op(atom_occ_op_t); + + cudaq::product_operator cavity_occ_op_t = + cudaq::matrix_operator::number(1); + cudaq::operator_sum cavity_occ_op(cavity_occ_op_t); + auto hamiltonian = 2 * M_PI * atom_occ_op + 2 * M_PI * cavity_occ_op + 2 * M_PI * 0.25 * (sm * a_dag + sm_dag * a); // auto matrix = hamiltonian.to_matrix(dims); @@ -160,6 +120,7 @@ TEST(EvolveTester, checkCompositeSystem) { cudaq::runge_kutta_integrator integrator; integrator.dt = 0.001; integrator.order = 4; + auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, integrator, {}, {&cavity_occ_op, &atom_occ_op}, true); @@ -185,8 +146,14 @@ TEST(EvolveTester, checkCompositeSystemWithCollapse) { auto sm = cudaq::matrix_operator::annihilate(0); auto sm_dag = cudaq::matrix_operator::create(0); - auto atom_occ_op = cudaq::matrix_operator::number(0); - auto cavity_occ_op = cudaq::matrix_operator::number(1); + cudaq::product_operator atom_occ_op_t = + cudaq::matrix_operator::number(0); + cudaq::operator_sum atom_occ_op(atom_occ_op_t); + + cudaq::product_operator cavity_occ_op_t = + cudaq::matrix_operator::number(1); + cudaq::operator_sum cavity_occ_op(cavity_occ_op_t); + auto hamiltonian = 2 * M_PI * atom_occ_op + 2 * M_PI * cavity_occ_op + 2 * M_PI * 0.25 * (sm * a_dag + sm_dag * a); // auto matrix = hamiltonian.to_matrix(dims); @@ -209,10 +176,12 @@ TEST(EvolveTester, checkCompositeSystemWithCollapse) { integrator.dt = 0.001; integrator.order = 4; constexpr double decayRate = 0.1; - auto collapsedOp = std::sqrt(decayRate) * a; - auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, - integrator, {&collapsedOp}, - {&cavity_occ_op, &atom_occ_op}, true); + cudaq::product_operator collapsedOp_t = + std::sqrt(decayRate) * a; + cudaq::operator_sum collapsedOp(collapsedOp_t); + cudaq::evolve_result result = cudaq::evolve_single( + hamiltonian, dims, schedule, initialState, integrator, {&collapsedOp}, + {&cavity_occ_op, &atom_occ_op}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); diff --git a/unittests/dynamics/test_helpers.cpp b/unittests/dynamics/test_helpers.cpp index dab8d441f4..e4aedcc3bf 100644 --- a/unittests/dynamics/test_helpers.cpp +++ b/unittests/dynamics/test_helpers.cpp @@ -6,63 +6,15 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/helpers.h" +#include "cudaq/dynamics/helpers.h" #include #include using namespace cudaq::detail; -TEST(OperatorHelpersTest, AggregateParameters_MultipleMappings) { - std::vector> mappings = { - {{"alpha", "Parameter A"}, {"beta", "Parameter B"}}, - {{"alpha", "Updated Parameter A"}, {"gamma", "New Parameter"}}}; - - auto result = aggregate_parameters(mappings); - - EXPECT_EQ(result["alpha"], "Parameter A\n---\nUpdated Parameter A"); - EXPECT_EQ(result["beta"], "Parameter B"); - EXPECT_EQ(result["gamma"], "New Parameter"); -} - -TEST(OperatorHelpersTest, AggregateParameters_EmptyMappings) { - std::vector> mappings; - auto result = aggregate_parameters(mappings); - - EXPECT_TRUE(result.empty()); -} - -TEST(OperatorHelpersTest, ParameterDocs_ValidExtraction) { - std::string docstring = "Description of function.\n" - "Arguments:\n" - " alpha (float): The first parameter.\n" - " beta (int): The second parameter."; - - auto result = parameter_docs("alpha", docstring); - EXPECT_EQ(result, "The first parameter."); - - result = parameter_docs("beta", docstring); - EXPECT_EQ(result, "The second parameter."); -} - -TEST(OperatorHelpersTest, ParameterDocs_InvalidParam) { - std::string docstring = "Description of function.\n" - "Arguments:\n" - " alpha (float): The first parameter.\n" - " beta (int): The second parameter."; - - auto result = parameter_docs("gamma", docstring); - EXPECT_EQ(result, ""); -} - -TEST(OperatorHelpersTest, ParameterDocs_EmptyDocString) { - std::string docstring = ""; - auto result = parameter_docs("alpha", docstring); - EXPECT_EQ(result, ""); -} - TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { std::vector degrees = {0, 1}; - std::map dimensions = {{0, 2}, {1, 2}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}}; auto states = generate_all_states(degrees, dimensions); std::vector expected_states = {"00", "01", "10", "11"}; @@ -72,7 +24,7 @@ TEST(OperatorHelpersTest, GenerateAllStates_TwoQubits) { TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { std::vector degrees = {0, 1, 2}; - std::map dimensions = {{0, 2}, {1, 2}, {2, 2}}; + std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 2}}; auto states = generate_all_states(degrees, dimensions); std::vector expected_states = {"000", "001", "010", "011", @@ -83,19 +35,12 @@ TEST(OperatorHelpersTest, GenerateAllStates_ThreeQubits) { TEST(OperatorHelpersTest, GenerateAllStates_EmptyDegrees) { std::vector degrees; - std::map dimensions; + std::unordered_map dimensions; auto states = generate_all_states(degrees, dimensions); EXPECT_TRUE(states.empty()); } -TEST(OperatorHelpersTest, GenerateAllStates_MissingDegreesInMap) { - std::vector degrees = {0, 1, 2}; - std::map dimensions = {{0, 2}, {1, 2}}; - - EXPECT_THROW(generate_all_states(degrees, dimensions), std::out_of_range); -} - TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { cudaq::matrix_2 matrix(2, 2); matrix[{0, 0}] = 1; @@ -106,15 +51,15 @@ TEST(OperatorHelpersTest, PermuteMatrix_SingleSwap) { // Swap rows and columns std::vector permutation = {1, 0}; - auto permuted_matrix = permute_matrix(matrix, permutation); + permute_matrix(matrix, permutation); cudaq::matrix_2 expected(2, 2); - matrix[{0, 0}] = 4; - matrix[{0, 1}] = 3; - matrix[{1, 0}] = 2; - matrix[{1, 1}] = 1; + expected[{0, 0}] = 4; + expected[{0, 1}] = 3; + expected[{1, 0}] = 2; + expected[{1, 1}] = 1; - EXPECT_EQ(permuted_matrix, expected); + EXPECT_EQ(matrix, expected); } TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { @@ -132,53 +77,25 @@ TEST(OperatorHelpersTest, PermuteMatrix_IdentityPermutation) { // No change std::vector permutation = {0, 1, 2}; - auto permuted_matrix = permute_matrix(matrix, permutation); + permute_matrix(matrix, permutation); - EXPECT_EQ(permuted_matrix, matrix); + EXPECT_EQ(matrix, matrix); } TEST(OperatorHelpersTest, CanonicalizeDegrees_SortedDescending) { std::vector degrees = {3, 1, 2}; - auto sorted = canonicalize_degrees(degrees); - EXPECT_EQ(sorted, (std::vector{3, 2, 1})); + canonicalize_degrees(degrees); + EXPECT_EQ(degrees, (std::vector{3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_AlreadySorted) { std::vector degrees = {5, 4, 3, 2, 1}; - auto sorted = canonicalize_degrees(degrees); - EXPECT_EQ(sorted, (std::vector{5, 4, 3, 2, 1})); + canonicalize_degrees(degrees); + EXPECT_EQ(degrees, (std::vector{5, 4, 3, 2, 1})); } TEST(OperatorHelpersTest, CanonicalizeDegrees_EmptyList) { std::vector degrees; - auto sorted = canonicalize_degrees(degrees); - EXPECT_TRUE(sorted.empty()); -} - -TEST(OperatorHelpersTest, ArgsFromKwargs_ValidArgs) { - std::map kwargs = { - {"alpha", "0.5"}, {"beta", "1.0"}, {"gamma", "2.0"}}; - - std::vector required_args = {"alpha", "beta"}; - std::vector kwonly_args = {"gamma"}; - - auto [args, kwonly] = args_from_kwargs(kwargs, required_args, kwonly_args); - - EXPECT_EQ(args.size(), 2); - EXPECT_EQ(args[0], "0.5"); - EXPECT_EQ(args[1], "1.0"); - - EXPECT_EQ(kwonly.size(), 1); - EXPECT_EQ(kwonly["gamma"], "2.0"); -} - -TEST(OperatorHelpersTest, ArgsFromKwargs_MissingRequiredArgs) { - std::map kwargs = {{"beta", "1.0"}, - {"gamma", "2.0"}}; - - std::vector required_args = {"alpha", "beta"}; - std::vector kwonly_args = {"gamma"}; - - EXPECT_THROW(args_from_kwargs(kwargs, required_args, kwonly_args), - std::invalid_argument); + canonicalize_degrees(degrees); + EXPECT_TRUE(degrees.empty()); } diff --git a/unittests/dynamics/test_mocks.h b/unittests/dynamics/test_mocks.h index 05ffc7243c..1ed71d5551 100644 --- a/unittests/dynamics/test_mocks.h +++ b/unittests/dynamics/test_mocks.h @@ -12,33 +12,10 @@ #include "cudaq/utils/tensor.h" #include #include -#include #include +#include #include -// Mock matrix_operator -inline cudaq::matrix_operator mock_matrix_operator(const std::string &op_id, - int qubit_index) { - try { - auto callback = [](std::vector dimensions, - std::map>) { - if (dimensions.size() != 1 || dimensions[0] != 2) { - throw std::invalid_argument("Invalid dimensions for operator."); - } - - cudaq::matrix_2 matrix(2, 2); - matrix[{0, 1}] = 1.0; - matrix[{1, 0}] = 1.0; - return matrix; - }; - - cudaq::matrix_operator::define(op_id, {-1}, callback); - } catch (...) { - } - - return cudaq::matrix_operator(op_id, {qubit_index}); -} - // Mock cudensitymatHandle_t inline cudensitymatHandle_t mock_handle() { cudensitymatHandle_t handle; diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index bf30fed12b..c32ce8f978 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -7,8 +7,8 @@ // ******************************************************************************/ #include "cudaq/runge_kutta_integrator.h" -#include "cudm_time_stepper.h" #include "cudm_state.h" +#include "cudm_time_stepper.h" #include "test_mocks.h" #include #include @@ -63,8 +63,8 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // auto integrator = std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - // 0.0, time_stepper_, 1); + // cudm_state(handle_, mock_initial_state_data(), + // mock_hilbert_space_dims()), 0.0, time_stepper_, 1); // integrator->set_option("dt", 0.1); // EXPECT_NO_THROW(integrator->integrate(1.0)); } @@ -72,8 +72,8 @@ TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { // auto midpointIntegrator = std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - // 0.0, time_stepper_, 2); + // cudm_state(handle_, mock_initial_state_data(), + // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); // midpointIntegrator->set_option("dt", 0.1); // EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); } @@ -112,8 +112,8 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { // auto integrator_missing_dt = std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), mock_hilbert_space_dims()), - // 0.0, time_stepper_, 2); + // cudm_state(handle_, mock_initial_state_data(), + // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); // EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); } @@ -155,27 +155,15 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { const std::vector> initialState = {{1.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {2}; - const std::string op_id = "pauli_x"; - auto func = [](std::vector dimensions, - std::map> _none) { - if (dimensions.size() != 1) - throw std::invalid_argument("Must have a singe dimension"); - if (dimensions[0] != 2) - throw std::invalid_argument("Must have dimension 2"); - auto mat = matrix_2(2, 2); - mat[{1, 0}] = 1.0; - mat[{0, 1}] = 1.0; - return mat; - }; - cudaq::matrix_operator::define(op_id, {-1}, func); + auto spin_op_x = cudaq::spin_operator::x(0); for (int integratorOrder : {1, 2, 4}) { std::cout << "Test RK order " << integratorOrder << "\n"; - auto op = cudaq::product_operator( - std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1, - cudaq::matrix_operator(op_id, {0})); + cudaq::product_operator ham1 = + (std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1 * spin_op_x); + cudaq::operator_sum ham(ham1); auto cudmOp = - helper.convert_to_cudensitymat_operator({}, op, + helper.convert_to_cudensitymat_operator({}, ham, dims); auto time_stepper = std::make_shared(handle_, cudmOp); diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp index 3cdbecc684..9fdb6b9900 100644 --- a/unittests/dynamics/utils.cpp +++ b/unittests/dynamics/utils.cpp @@ -6,24 +6,24 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include -#include "cudaq/operators.h" -#include "cudaq/dynamics/matrix_operators.h" namespace utils { void print(cudaq::matrix_2 mat) { for (std::size_t i = 0; i < mat.get_rows(); i++) { - for (std::size_t j = 0; j < mat.get_columns(); j++) - std::cout << mat[{i, j}] << " "; + for (std::size_t j = 0; j < mat.get_columns(); j++) + std::cout << mat[{i, j}] << " "; std::cout << std::endl; } } -void assert_product_equal(const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms) { +void assert_product_equal( + const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms) { cudaq::operator_sum sum = got; ASSERT_TRUE(sum.get_terms().size() == 1); ASSERT_TRUE(got.get_coefficient().evaluate() == expected_coefficient); @@ -124,14 +124,14 @@ cudaq::matrix_2 squeeze_matrix(std::size_t size, } cudaq::matrix_2 PauliX_matrix() { - auto mat = cudaq::matrix_2(2,2); + auto mat = cudaq::matrix_2(2, 2); mat[{0, 1}] = 1.0; mat[{1, 0}] = 1.0; return mat; } cudaq::matrix_2 PauliZ_matrix() { - auto mat = cudaq::matrix_2(2,2); + auto mat = cudaq::matrix_2(2, 2); mat[{0, 0}] = 1.0; mat[{1, 1}] = -1.0; return mat; diff --git a/unittests/dynamics/utils.h b/unittests/dynamics/utils.h index fa04383d56..fcf5c74202 100644 --- a/unittests/dynamics/utils.h +++ b/unittests/dynamics/utils.h @@ -6,17 +6,17 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/utils/tensor.h" #include "cudaq/operators.h" -#include "cudaq/dynamics/matrix_operators.h" +#include "cudaq/utils/tensor.h" namespace utils { void print(cudaq::matrix_2 mat); -void assert_product_equal(const cudaq::product_operator &got, - const std::complex &expected_coefficient, - const std::vector &expected_terms); +void assert_product_equal( + const cudaq::product_operator &got, + const std::complex &expected_coefficient, + const std::vector &expected_terms); void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b); From f2ba966b4b302af55af5f0d1d11c4ffae107521a Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Thu, 13 Feb 2025 23:06:37 +0000 Subject: [PATCH 273/311] changes to the test script Signed-off-by: Bettina Heim --- main.cpp | 628 ++++++++++++++++++++++---------------- old.perf | 104 +++++++ runtime/cudaq/operators.h | 12 +- v1.perf | 106 +++++++ 4 files changed, 578 insertions(+), 272 deletions(-) create mode 100644 old.perf create mode 100644 v1.perf diff --git a/main.cpp b/main.cpp index 38362d25e8..4b062deb8e 100644 --- a/main.cpp +++ b/main.cpp @@ -14,272 +14,360 @@ int main() { + bool run_old = false; + bool run_new = true; + + std::vector repetitions = {100, 1000, 10000}; + // multiplication inplace with itself + std::cout << std::endl << "multiplication inplace with itself" << std::endl; + + for (auto nr_reps : repetitions) { cudaq::spin_op spin_op = cudaq::spin_op(); cudaq::product_operator prod_op = cudaq::spin_operator::identity(); - int nr_reps = 1000; - std::cout << "multiplication inplace with itself" << std::endl; - - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(0); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } + std::cout << std::endl << "multiplication inplace with other" << std::endl; // multiplication inplace with other - spin_op = cudaq::spin_op(); - prod_op = cudaq::spin_operator::identity(); - - nr_reps = 1000; - std::cout << "multiplication inplace with other" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::product_operator prod_op = cudaq::spin_operator::identity(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // multiplication inplace with other (reverse order) - - spin_op = cudaq::spin_op(); - prod_op = cudaq::spin_operator::identity(); - - nr_reps = 1000; - std::cout << "multiplication inplace with other (reverse order)" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "multiplication inplace with other (reverse order)" << std::endl; + + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::product_operator prod_op = cudaq::spin_operator::identity(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // addition inplace with itself - - spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); - - nr_reps = 1000; - std::cout << "addition inplace with itself" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(0); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(0); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "addition inplace with itself" << std::endl; + + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // addition inplace with other - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::empty(); - - nr_reps = 1000; - std::cout << "addition inplace with other" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "addition inplace with other" << std::endl; + + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old && nr_reps <= 1000) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // addition inplace with other (reverse order) - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::empty(); - - nr_reps = 1000; - std::cout << "addition inplace with other (reverse order)" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(nr_reps - i); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "addition inplace with other (reverse order)" << std::endl; + + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old && nr_reps <= 1000) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // addition inplace with product self - - auto spin_prod = cudaq::spin_op(); - for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(i); - spin_op = spin_prod; - prod_op = cudaq::spin_operator::identity(); - for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(i); - op_sum = prod_op; - - nr_reps = 1000; - std::cout << "addition inplace with product self" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "addition inplace with product self" << std::endl; + + for (auto nr_reps : repetitions) { + + auto spin_prod = cudaq::spin_op(); + for (auto i = 0; i < 100; ++i) + spin_prod *= cudaq::spin::x(i); + cudaq::spin_op spin_op = spin_prod; + auto prod_op = cudaq::spin_operator::identity(); + for (auto i = 0; i < 100; ++i) + prod_op *= cudaq::spin_operator::x(i); + cudaq::operator_sum op_sum = prod_op; + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // addition inplace with product self (reverse order) - - spin_prod = cudaq::spin_op(); - for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(100 - i); - spin_op = spin_prod; - prod_op = cudaq::spin_operator::identity(); - for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(100 - i); - op_sum = prod_op; - - nr_reps = 1000; - std::cout << "addition inplace with product self" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "addition inplace with product self (reverse order)" << std::endl; + + for (auto nr_reps : repetitions) { + + auto spin_prod = cudaq::spin_op(); + for (auto i = 0; i < 100; ++i) + spin_prod *= cudaq::spin::x(100 - i); + cudaq::spin_op spin_op = spin_prod; + auto prod_op = cudaq::spin_operator::identity(); + for (auto i = 0; i < 100; ++i) + prod_op *= cudaq::spin_operator::x(100 - i); + cudaq::operator_sum op_sum = prod_op; + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // product inplace with 2-term sum on fixed degrees + std::cout << std::endl << "product inplace with 2-term sum on fixed degrees" << std::endl; + + for (auto nr_reps : repetitions) { + + auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); + cudaq::spin_op spin_op = spin_term; + auto prod_term = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(1); + cudaq::operator_sum op_sum = prod_term; + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= spin_term; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= prod_term; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } - auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); - spin_op = spin_term; - auto prod_term = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(1); - op_sum = prod_term; - nr_reps = 20; - std::cout << "product inplace with 2-term sum on fixed degrees" << std::endl; - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= spin_term; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; + repetitions = {10, 20, 30}; - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= prod_term; - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; // product inplace with 2-term sum on varying degrees - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::identity(); - - nr_reps = 20; - std::cout << "product inplace with 2-term sum on varying degrees" - << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "product inplace with 2-term sum on varying degrees" << std::endl; + + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); + op_sum = cudaq::spin_operator::identity(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old && nr_reps <= 20) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new && nr_reps <= 20) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // product inplace with 2-term sum on varying degrees (reverse order) - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::identity(); - - nr_reps = 20; - std::cout - << "product inplace with 2-term sum on varying degrees (reverse order)" - << std::endl; - - /* - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - - 1)); stop = std::chrono::high_resolution_clock::now(); duration = - std::chrono::duration(stop - start); std::cout << "Old setup took " << - duration.count() << " seconds.\n"; - */ - std::cout << "Old setup segfaults" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(nr_reps - i) + - cudaq::spin_operator::z(nr_reps - i - 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << std::endl << "product inplace with 2-term sum on varying degrees (reverse order)" << std::endl; + + for (auto nr_reps : repetitions) { + + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); // fixme: only const & constructor is public + op_sum = cudaq::spin_operator::identity(); + + std::cout << "nr ops: " << nr_reps << std::endl; + + if (run_old && nr_reps <= 20) { + /* + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - 1)); + stop = std::chrono::high_resolution_clock::now(); + duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + */ + std::cout << "Old setup segfaults" << std::endl; + } + + if (run_new && nr_reps <= 20) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(nr_reps - i) + cudaq::spin_operator::z(nr_reps - i - 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } // sum of products of random terms + std::cout << std::endl << "sum of products of random terms" << std::endl; + std::vector nr_sum_terms = { 10, 100, 1000 }; + std::vector nr_product_terms = { 100, 1000, 10000 }; auto nr_degrees = 100; std::vector spin_ops; for (auto i = 0; i < nr_degrees; ++i) { @@ -295,43 +383,49 @@ int main() { leaf_ops.push_back(cudaq::spin_operator::z(i)); leaf_ops.push_back(cudaq::spin_operator::i(i)); } - - auto term_length = 1000; - auto nr_terms = 200; srand(5); // random number seed - std::vector> indices; - for (auto i = 0; i < nr_terms; ++i) { - indices.push_back({}); - for (auto j = 0; j < term_length; ++j) - indices[i].push_back(rand() % 400); - } - - spin_op = cudaq::spin_op(); - op_sum = cudaq::spin_operator::empty(); - std::cout << "sum of products of random terms" << std::endl; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_terms; ++i) { - auto term = cudaq::spin_op(); - for (auto j = 0; j < term_length; ++j) - term *= spin_ops[indices[i][j]]; - spin_op += term; - } - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_terms; ++i) { - auto term = cudaq::spin_operator::identity(); - for (auto j = 0; j < term_length; ++j) - term *= leaf_ops[indices[i][j]]; - op_sum += term; + for (auto nr_sums : nr_sum_terms) { + for (auto nr_prods : nr_product_terms) { + + std::vector> indices; + for (auto i = 0; i < nr_sums; ++i) { + indices.push_back({}); + for (auto j = 0; j < nr_prods; ++j) + indices[i].push_back(rand() % 400); + } + + std::cout << "nr terms " << nr_sums << ", term length " << nr_prods << std::endl; + + auto spin_op = cudaq::spin_op(); + auto op_sum = cudaq::spin_operator::empty(); + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_sums; ++i) { + auto term = cudaq::spin_op(); + for (auto j = 0; j < nr_prods; ++j) + term *= spin_ops[indices[i][j]]; + spin_op += term; + } + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_sums; ++i) { + auto term = cudaq::spin_operator::identity(); + for (auto j = 0; j < nr_prods; ++j) + term *= leaf_ops[indices[i][j]]; + op_sum += term; + } + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } + } } - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - return 0; } \ No newline at end of file diff --git a/old.perf b/old.perf new file mode 100644 index 0000000000..e78b18971c --- /dev/null +++ b/old.perf @@ -0,0 +1,104 @@ + +multiplication inplace with itself +nr ops: 100 +Old setup took 0.000989452 seconds. +nr ops: 1000 +Old setup took 0.00629858 seconds. +nr ops: 10000 +Old setup took 0.0611419 seconds. + +multiplication inplace with other +nr ops: 100 +Old setup took 0.00300721 seconds. +nr ops: 1000 +Old setup took 0.200118 seconds. +nr ops: 10000 +Old setup took 18.4632 seconds. + +multiplication inplace with other (reverse order) +nr ops: 100 +Old setup took 0.00385455 seconds. +nr ops: 1000 +Old setup took 0.26617 seconds. +nr ops: 10000 +Old setup took 26.2626 seconds. + +addition inplace with itself +nr ops: 100 +Old setup took 0.000757876 seconds. +nr ops: 1000 +Old setup took 0.00587647 seconds. +nr ops: 10000 +Old setup took 0.059795 seconds. + +addition inplace with other +nr ops: 100 +Old setup took 0.0500884 seconds. +nr ops: 1000 +Old setup took 19.3641 seconds. +nr ops: 10000 + +addition inplace with other (reverse order) +nr ops: 100 +Old setup took 0.0191391 seconds. +nr ops: 1000 +Old setup took 15.3959 seconds. +nr ops: 10000 + +addition inplace with product self +nr ops: 100 +Old setup took 0.00115114 seconds. +nr ops: 1000 +Old setup took 0.0128529 seconds. +nr ops: 10000 +Old setup took 0.126899 seconds. + +addition inplace with product self (reverse order) +nr ops: 100 +Old setup took 0.002065 seconds. +nr ops: 1000 +Old setup took 0.0172566 seconds. +nr ops: 10000 +Old setup took 0.123577 seconds. + +product inplace with 2-term sum on fixed degrees +nr ops: 100 +Old setup took 0.00136818 seconds. +nr ops: 1000 +Old setup took 0.0159856 seconds. +nr ops: 10000 +Old setup took 0.140271 seconds. + +product inplace with 2-term sum on varying degrees +nr ops: 10 +Old setup took 0.0362399 seconds. +nr ops: 20 +Old setup took 11.1247 seconds. +nr ops: 30 + +product inplace with 2-term sum on varying degrees (reverse order) +nr ops: 10 +Old setup segfaults +nr ops: 20 +Old setup segfaults +nr ops: 30 + +sum of products of random terms +nr terms 10, term length 100 +Old setup took 0.0286609 seconds. +nr terms 10, term length 1000 +Old setup took 0.30442 seconds. +nr terms 10, term length 10000 +Old setup took 3.1964 seconds. +nr terms 100, term length 100 +Old setup took 0.319011 seconds. +nr terms 100, term length 1000 +Old setup took 3.07269 seconds. +nr terms 100, term length 10000 +Old setup took 30.7527 seconds. +nr terms 1000, term length 100 +Old setup took 3.64059 seconds. +nr terms 1000, term length 1000 +Old setup took 31.0172 seconds. +nr terms 1000, term length 10000 +Old setup took 311.449 seconds. diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index c64e50a20f..a96d98dced 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,11 +8,14 @@ #pragma once +#include +#include +#include + +#include "utils/tensor.h" #include "dynamics/operator_leafs.h" #include "dynamics/templates.h" #include "utils/tensor.h" -#include -#include namespace cudaq { @@ -255,9 +258,8 @@ class product_operator { #if !defined(NDEBUG) bool is_canonicalized() const; #endif - - std::vector::const_iterator - find_insert_at(const HandlerTy &other) const; + + typename std::vector::const_iterator find_insert_at(const HandlerTy &other) const; template ::value && diff --git a/v1.perf b/v1.perf new file mode 100644 index 0000000000..88c5540841 --- /dev/null +++ b/v1.perf @@ -0,0 +1,106 @@ + +multiplication inplace with itself +nr ops: 100 +New setup took 0.000605938 seconds. +nr ops: 1000 +New setup took 0.00326204 seconds. +nr ops: 10000 +New setup took 0.0268849 seconds. + +multiplication inplace with other +nr ops: 100 +New setup took 0.00259074 seconds. +nr ops: 1000 +New setup took 0.178419 seconds. +nr ops: 10000 +New setup took 17.7425 seconds. + +multiplication inplace with other (reverse order) +nr ops: 100 +New setup took 0.000488501 seconds. +nr ops: 1000 +New setup took 0.0257551 seconds. +nr ops: 10000 +New setup took 2.63377 seconds. + +addition inplace with itself +nr ops: 100 +New setup took 0.000423601 seconds. +nr ops: 1000 +New setup took 0.00248007 seconds. +nr ops: 10000 +New setup took 0.0197457 seconds. + +addition inplace with other +nr ops: 100 +New setup took 0.000704163 seconds. +nr ops: 1000 +New setup took 0.0130536 seconds. +nr ops: 10000 +New setup took 1.08428 seconds. + +addition inplace with other (reverse order) +nr ops: 100 +New setup took 0.000478452 seconds. +nr ops: 1000 +New setup took 0.0126688 seconds. +nr ops: 10000 +New setup took 1.03774 seconds. + +addition inplace with product self +nr ops: 100 +New setup took 0.000741696 seconds. +nr ops: 1000 +New setup took 0.00571034 seconds. +nr ops: 10000 +New setup took 0.0663077 seconds. + +addition inplace with product self (reverse order) +nr ops: 100 +New setup took 0.000913494 seconds. +nr ops: 1000 +New setup took 0.0076885 seconds. +nr ops: 10000 +New setup took 0.0662493 seconds. + +product inplace with 2-term sum on fixed degrees +nr ops: 100 +New setup took 0.0125319 seconds. +nr ops: 1000 +New setup took 0.0631849 seconds. +nr ops: 10000 +New setup took 0.740012 seconds. + +product inplace with 2-term sum on varying degrees +nr ops: 10 +New setup took 0.0349756 seconds. +nr ops: 20 +New setup took 65.7295 seconds. +nr ops: 30 + +product inplace with 2-term sum on varying degrees (reverse order) +nr ops: 10 +New setup took 0.028038 seconds. +nr ops: 20 +New setup took 56.2411 seconds. +nr ops: 30 + +sum of products of random terms +nr terms 10, term length 100 +New setup took 0.00712887 seconds. +nr terms 10, term length 1000 +New setup took 0.146541 seconds. +nr terms 10, term length 10000 +New setup took 1.54605 seconds. +nr terms 100, term length 100 +New setup took 0.0734078 seconds. +nr terms 100, term length 1000 +New setup took 1.41369 seconds. +nr terms 100, term length 10000 +New setup took 15.3141 seconds. +nr terms 1000, term length 100 +New setup took 0.849971 seconds. +nr terms 1000, term length 1000 +New setup took 14.1757 seconds. +nr terms 1000, term length 10000 +New setup took 152.821 seconds. From b5ada26121a8c53cdf5f636310d3472047c6a69d Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 14 Feb 2025 10:12:51 +0000 Subject: [PATCH 274/311] checking in v2 perf numbers for comparison Signed-off-by: Bettina Heim --- v2.perf | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 v2.perf diff --git a/v2.perf b/v2.perf new file mode 100644 index 0000000000..7ed7efe5b9 --- /dev/null +++ b/v2.perf @@ -0,0 +1,106 @@ + +multiplication inplace with itself +nr ops: 100 +New setup took 0.00101147 seconds. +nr ops: 1000 +New setup took 0.00690629 seconds. +nr ops: 10000 +New setup took 0.0446706 seconds. + +multiplication inplace with other +nr ops: 100 +New setup took 0.00393387 seconds. +nr ops: 1000 +New setup took 0.284029 seconds. +nr ops: 10000 +New setup took 27.0573 seconds. + +multiplication inplace with other (reverse order) +nr ops: 100 +New setup took 0.00152094 seconds. +nr ops: 1000 +New setup took 0.201935 seconds. +nr ops: 10000 +New setup took 9.19236 seconds. + +addition inplace with itself +nr ops: 100 +New setup took 0.000482909 seconds. +nr ops: 1000 +New setup took 0.00343451 seconds. +nr ops: 10000 +New setup took 0.0370845 seconds. + +addition inplace with other +nr ops: 100 +New setup took 0.00056023 seconds. +nr ops: 1000 +New setup took 0.0047066 seconds. +nr ops: 10000 +New setup took 0.0604502 seconds. + +addition inplace with other (reverse order) +nr ops: 100 +New setup took 0.000840673 seconds. +nr ops: 1000 +New setup took 0.00583381 seconds. +nr ops: 10000 +New setup took 0.0606676 seconds. + +addition inplace with product self +nr ops: 100 +New setup took 0.00156973 seconds. +nr ops: 1000 +New setup took 0.0138318 seconds. +nr ops: 10000 +New setup took 0.114067 seconds. + +addition inplace with product self (reverse order) +nr ops: 100 +New setup took 0.00111965 seconds. +nr ops: 1000 +New setup took 0.0144028 seconds. +nr ops: 10000 +New setup took 0.138953 seconds. + +product inplace with 2-term sum on fixed degrees +nr ops: 100 +New setup took 0.0124617 seconds. +nr ops: 1000 +New setup took 0.0745379 seconds. +nr ops: 10000 +New setup took 0.585963 seconds. + +product inplace with 2-term sum on varying degrees +nr ops: 10 +New setup took 0.0314756 seconds. +nr ops: 20 +New setup took 60.9737 seconds. +nr ops: 30 + +product inplace with 2-term sum on varying degrees (reverse order) +nr ops: 10 +New setup took 0.0289655 seconds. +nr ops: 20 +New setup took 49.7242 seconds. +nr ops: 30 + +sum of products of random terms +nr terms 10, term length 100 +New setup took 0.0107945 seconds. +nr terms 10, term length 1000 +New setup took 0.192907 seconds. +nr terms 10, term length 10000 +New setup took 2.04548 seconds. +nr terms 100, term length 100 +New setup took 0.117314 seconds. +nr terms 100, term length 1000 +New setup took 1.93779 seconds. +nr terms 100, term length 10000 +New setup took 20.3838 seconds. +nr terms 1000, term length 100 +New setup took 1.14292 seconds. +nr terms 1000, term length 1000 +New setup took 19.3147 seconds. +nr terms 1000, term length 10000 +New setup took 204.79 seconds. From 79a9b155a070013f36fe17bce269c3b4466845dd Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 14 Feb 2025 13:08:33 +0000 Subject: [PATCH 275/311] Bringing in more commits from Bettina's branch Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/matrix_operators.cpp | 11 +- runtime/cudaq/dynamics/operator_sum.cpp | 318 +++++++++++++------- runtime/cudaq/operators.h | 54 ++-- 3 files changed, 246 insertions(+), 137 deletions(-) diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 4954a80d5e..576dac75ab 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -130,14 +130,9 @@ matrix_operator::matrix_operator(const T &other) { this->op_code = type_prefix + other.to_string(false) + std::to_string(this->targets.size()); this->id = type_prefix + other.unique_id(); - for (auto t : this->targets) - this->id += std::to_string(t); - if (matrix_operator::m_ops.find(this->op_code) == - matrix_operator::m_ops.end()) { - auto func = [targets = other.degrees(), other]( - const std::vector &dimensions, - const std::unordered_map> - &_none) { + if (matrix_operator::m_ops.find(this->op_code) == matrix_operator::m_ops.end()) { + auto func = [targets = other.degrees(), other] + (const std::vector &dimensions, const std::unordered_map> &_none) { std::unordered_map dims; for (auto i = 0; i < dimensions.size(); ++i) dims[targets[i]] = dimensions[i]; diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index bde0178f21..740cc023c2 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -171,22 +171,21 @@ INSTANTIATE_SUM_PROPERTIES(boson_operator); template operator_sum::operator_sum(const product_operator &prod) { + this->tmap.max_load_factor(0.25); this->insert(prod); } -template -template , Args>...>::value, - bool>> -operator_sum::operator_sum(Args &&...args) { +template +template, Args>...>::value, bool>> +operator_sum::operator_sum(Args&&... args) { + this->tmap.max_load_factor(0.25); this->tmap.reserve(sizeof...(Args)); aggregate_terms(std::forward &&>(args)...); } -template -operator_sum::operator_sum( - std::vector> &&terms) { +template +operator_sum::operator_sum(std::vector> &&terms) { + this->tmap.max_load_factor(0.25); this->tmap.reserve(terms.size()); for (auto &&term : terms) this->insert(std::move(term)); @@ -198,6 +197,7 @@ template ::value, bool>> operator_sum::operator_sum(const operator_sum &other) { + this->tmap.max_load_factor(0.25); this->tmap.reserve(other.tmap.size()); for (const auto &entry : other.tmap) { product_operator prod(entry.second); @@ -207,11 +207,15 @@ operator_sum::operator_sum(const operator_sum &other) { template operator_sum::operator_sum(const operator_sum &other) - : tmap(other.tmap) {} + : tmap(other.tmap) { + this->tmap.max_load_factor(0.25); + } -template -operator_sum::operator_sum(operator_sum &&other) - : tmap(std::move(other.tmap)) {} +template +operator_sum::operator_sum(operator_sum &&other) + : tmap(std::move(other.tmap)) { + this->tmap.max_load_factor(0.25); // probably not needed + } #define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ \ @@ -295,6 +299,7 @@ operator_sum & operator_sum::operator=(const operator_sum &other) { if (this != &other) { this->tmap = other.tmap; + this->tmap.max_load_factor(0.25); // probably not needed } return *this; } @@ -304,6 +309,7 @@ operator_sum & operator_sum::operator=(operator_sum &&other) { if (this != &other) { this->tmap = std::move(other.tmap); + this->tmap.max_load_factor(0.25); // probably not needed } return *this; } @@ -343,7 +349,11 @@ INSTANTIATE_SUM_ASSIGNMENTS(boson_operator); template std::string operator_sum::to_string() const { - throw std::runtime_error("not implemented"); + auto it = this->tmap.cbegin(); + std::string str = it->second.to_string(); + while (++it != this->tmap.cend()) + str += " + " + it->second.to_string(); + return std::move(str); } template @@ -378,22 +388,32 @@ template operator_sum operator_sum::operator-() && { for (auto &entry : this->tmap) entry.second.coefficient *= -1; - return *this; + return std::move(*this); } template -operator_sum operator_sum::operator+() const { +operator_sum operator_sum::operator+() const & { return *this; } -#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ - \ - template operator_sum operator_sum::operator-() \ - const &; \ - \ - template operator_sum operator_sum::operator-() &&; \ - \ - template operator_sum operator_sum::operator+() const; +template +operator_sum operator_sum::operator+() && { + return std::move(*this); +} + +#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ + \ + template \ + operator_sum operator_sum::operator-() const &; \ + \ + template \ + operator_sum operator_sum::operator-() &&; \ + \ + template \ + operator_sum operator_sum::operator+() const &; \ + \ + template \ + operator_sum operator_sum::operator+() &&; \ INSTANTIATE_SUM_UNARY_OPS(matrix_operator); INSTANTIATE_SUM_UNARY_OPS(spin_operator); @@ -401,29 +421,39 @@ INSTANTIATE_SUM_UNARY_OPS(boson_operator); // right-hand arithmetics -#define SUM_MULTIPLICATION(otherTy) \ - template \ - operator_sum operator_sum::operator*(otherTy other) \ - const { \ - operator_sum sum; \ - sum.tmap.reserve(this->tmap.size()); \ - for (const auto &entry : this->tmap) \ - sum.insert(other *entry.second); \ - return sum; \ +#define SUM_MULTIPLICATION(otherTy) \ + \ + template \ + operator_sum operator_sum::operator*(otherTy other) const & { \ + operator_sum sum; \ + sum.tmap.reserve(this->tmap.size()); \ + for (const auto &entry : this->tmap) \ + sum.insert(other * entry.second); \ + return sum; \ + } \ + \ + template \ + operator_sum operator_sum::operator*(otherTy other) && { \ + return std::move(*this *= other); \ } SUM_MULTIPLICATION(double); SUM_MULTIPLICATION(std::complex); SUM_MULTIPLICATION(const scalar_operator &); -#define SUM_ADDITION(otherTy, op) \ - template \ - operator_sum operator_sum::operator op(otherTy other) \ - const { \ - operator_sum sum(*this); \ - sum.insert(product_operator(op other)); \ - return sum; \ - } +#define SUM_ADDITION(otherTy, op) \ + \ + template \ + operator_sum operator_sum::operator op(otherTy other) const & { \ + operator_sum sum(*this); \ + sum.insert(product_operator(op other)); \ + return sum; \ + } \ + \ + template \ + operator_sum operator_sum::operator op(otherTy other) && { \ + return std::move(*this op##= other); \ + } \ SUM_ADDITION(double, +); SUM_ADDITION(double, -); @@ -432,34 +462,51 @@ SUM_ADDITION(std::complex, -); SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); -#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ - \ - template operator_sum operator_sum::operator*( \ - double other) const; \ - template operator_sum operator_sum::operator+( \ - double other) const; \ - template operator_sum operator_sum::operator-( \ - double other) const; \ - template operator_sum operator_sum::operator*( \ - std::complex other) const; \ - template operator_sum operator_sum::operator+( \ - std::complex other) const; \ - template operator_sum operator_sum::operator-( \ - std::complex other) const; \ - template operator_sum operator_sum::operator*( \ - const scalar_operator &other) const; \ - template operator_sum operator_sum::operator+( \ - const scalar_operator &other) const; \ - template operator_sum operator_sum::operator-( \ - const scalar_operator &other) const; +#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ + \ + template \ + operator_sum operator_sum::operator*(double other) const &; \ + template \ + operator_sum operator_sum::operator*(double other) &&; \ + template \ + operator_sum operator_sum::operator+(double other) const &; \ + template \ + operator_sum operator_sum::operator+(double other) &&; \ + template \ + operator_sum operator_sum::operator-(double other) const &; \ + template \ + operator_sum operator_sum::operator-(double other) &&; \ + template \ + operator_sum operator_sum::operator*(std::complex other) const &; \ + template \ + operator_sum operator_sum::operator*(std::complex other) &&; \ + template \ + operator_sum operator_sum::operator+(std::complex other) const &; \ + template \ + operator_sum operator_sum::operator+(std::complex other) &&; \ + template \ + operator_sum operator_sum::operator-(std::complex other) const &; \ + template \ + operator_sum operator_sum::operator-(std::complex other) &&; \ + template \ + operator_sum operator_sum::operator*(const scalar_operator &other) const &; \ + template \ + operator_sum operator_sum::operator*(const scalar_operator &other) &&; \ + template \ + operator_sum operator_sum::operator+(const scalar_operator &other) const &; \ + template \ + operator_sum operator_sum::operator+(const scalar_operator &other) &&; \ + template \ + operator_sum operator_sum::operator-(const scalar_operator &other) const &; \ + template \ + operator_sum operator_sum::operator-(const scalar_operator &other) &&; INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template -operator_sum operator_sum::operator*( - const product_operator &other) const { +operator_sum operator_sum::operator*(const product_operator &other) const & { operator_sum sum; sum.tmap.reserve(this->tmap.size()); for (const auto &entry : this->tmap) @@ -467,21 +514,32 @@ operator_sum operator_sum::operator*( return sum; } -#define SUM_ADDITION_PRODUCT(op) \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) const { \ - operator_sum sum(*this); \ - sum.insert(op other); \ - return sum; \ - } +template +operator_sum operator_sum::operator*(const product_operator &other) && { + return std::move(*this *= other); +} + +#define SUM_ADDITION_PRODUCT(op) \ + \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const & { \ + operator_sum sum(*this); \ + sum.insert(op other); \ + return sum; \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) && { \ + return std::move(*this op##= other); \ + } SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum -operator_sum::operator*(const operator_sum &other) const { +operator_sum operator_sum::operator*(const operator_sum &other) const & { operator_sum sum; sum.tmap.reserve(this->tmap.size() * other.tmap.size()); for (const auto &entry_self : this->tmap) { @@ -491,36 +549,71 @@ operator_sum::operator*(const operator_sum &other) const { return sum; } -#define SUM_ADDITION_SUM(op) \ - template \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) const { \ - operator_sum sum; \ - sum.tmap.reserve(this->tmap.size() + other.tmap.size()); \ - for (const auto &entry : this->tmap) \ - sum.tmap.insert(entry); \ - for (const auto &entry : other.tmap) \ - sum.insert(op entry.second); \ - return sum; \ - } +template +operator_sum operator_sum::operator*(const operator_sum &other) && { + return std::move(*this *= other); +} + +#define SUM_ADDITION_SUM(op) \ + \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const & { \ + operator_sum sum; \ + sum.tmap.reserve(this->tmap.size() + other.tmap.size()); \ + for (const auto &entry : this->tmap) \ + sum.tmap.insert(entry); \ + for (const auto &entry : other.tmap) \ + sum.insert(op entry.second); \ + return sum; \ + } \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) && { \ + return std::move(*this op##= other); \ + } \ SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); -#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ - \ - template operator_sum operator_sum::operator*( \ - const product_operator &other) const; \ - template operator_sum operator_sum::operator+( \ - const product_operator &other) const; \ - template operator_sum operator_sum::operator-( \ - const product_operator &other) const; \ - template operator_sum operator_sum::operator*( \ - const operator_sum &other) const; \ - template operator_sum operator_sum::operator+( \ - const operator_sum &other) const; \ - template operator_sum operator_sum::operator-( \ - const operator_sum &other) const; +#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + operator_sum operator_sum::operator*( \ + const product_operator &other) const &; \ + template \ + operator_sum operator_sum::operator*( \ + const product_operator &other) &&; \ + template \ + operator_sum operator_sum::operator+( \ + const product_operator &other) const &; \ + template \ + operator_sum operator_sum::operator+( \ + const product_operator &other) &&; \ + template \ + operator_sum operator_sum::operator-( \ + const product_operator &other) const &; \ + template \ + operator_sum operator_sum::operator-( \ + const product_operator &other) &&; \ + template \ + operator_sum operator_sum::operator*( \ + const operator_sum &other) const &; \ + template \ + operator_sum operator_sum::operator*( \ + const operator_sum &other) &&; \ + template \ + operator_sum operator_sum::operator+( \ + const operator_sum &other) const &; \ + template \ + operator_sum operator_sum::operator+( \ + const operator_sum &other) &&; \ + template \ + operator_sum operator_sum::operator-( \ + const operator_sum &other) const &; \ + template \ + operator_sum operator_sum::operator-( \ + const operator_sum &other) &&; INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); @@ -555,13 +648,15 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template -operator_sum & -operator_sum::operator*=(const product_operator &other) { - operator_sum sum; - sum.tmap.reserve(this->tmap.size()); - for (auto &entry : this->tmap) - sum.insert(entry.second *= other); - *this = std::move(sum); +operator_sum& operator_sum::operator*=(const product_operator &other) { + std::vector> prods; + prods.reserve(this->tmap.size()); + for (auto it = this->tmap.begin(); it != this->tmap.end();) { + prods.push_back(std::move(it->second *= other)); + it = this->tmap.erase(it); + } + for (auto &&prod : prods) + this->insert(std::move(prod)); return *this; } @@ -580,7 +675,16 @@ template operator_sum & operator_sum::operator*=(const operator_sum &other) { this->tmap.reserve(this->tmap.size() * other.tmap.size()); - *this = *this * other; // we need to update all entries anyway + std::vector> prods; + prods.reserve(this->tmap.size()); + for (auto it = this->tmap.cbegin(); it != this->tmap.cend();) { + prods.push_back(std::move(it->second)); + it = this->tmap.erase(it); + } + for (const auto &prod_self : prods) { + for (const auto &entry_other : other.tmap) + this->insert(prod_self * entry_other.second); + } return *this; } diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index a96d98dced..6635030afa 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -22,9 +22,6 @@ namespace cudaq { class MatrixArithmetics; class EvaluatedMatrix; -// fixme: write overloads with rvalue qualifiers -// https://stackoverflow.com/questions/37737798/c-is-it-possible-to-overload-the-unary-minus-operator-of-an-rvalue-reference - /// @brief Represents an operator expression consisting of a sum of terms, where /// each term is a product of elementary and scalar operators. Operator /// expressions cannot be used within quantum kernels, but they provide methods @@ -137,28 +134,41 @@ class operator_sum { operator_sum operator-() const &; operator_sum operator-() &&; - operator_sum operator+() const; + operator_sum operator+() const &; + operator_sum operator+() &&; // right-hand arithmetics - operator_sum operator*(double other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - operator_sum operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - operator_sum operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - operator_sum - operator*(const product_operator &other) const; - operator_sum - operator+(const product_operator &other) const; - operator_sum - operator-(const product_operator &other) const; - operator_sum operator+(const operator_sum &other) const; - operator_sum operator-(const operator_sum &other) const; - operator_sum operator*(const operator_sum &other) const; + operator_sum operator*(double other) const &; + operator_sum operator*(double other) &&; + operator_sum operator+(double other) const &; + operator_sum operator+(double other) &&; + operator_sum operator-(double other) const &; + operator_sum operator-(double other) &&; + operator_sum operator*(std::complex other) const &; + operator_sum operator*(std::complex other) &&; + operator_sum operator+(std::complex other) const &; + operator_sum operator+(std::complex other) &&; + operator_sum operator-(std::complex other) const &; + operator_sum operator-(std::complex other) &&; + operator_sum operator*(const scalar_operator &other) const &; + operator_sum operator*(const scalar_operator &other) &&; + operator_sum operator+(const scalar_operator &other) const &; + operator_sum operator+(const scalar_operator &other) &&; + operator_sum operator-(const scalar_operator &other) const &; + operator_sum operator-(const scalar_operator &other) &&; + operator_sum operator*(const product_operator &other) const &; + operator_sum operator*(const product_operator &other) &&; + operator_sum operator+(const product_operator &other) const &; + operator_sum operator+(const product_operator &other) &&; + operator_sum operator-(const product_operator &other) const &; + operator_sum operator-(const product_operator &other) &&; + operator_sum operator*(const operator_sum &other) const &; + operator_sum operator*(const operator_sum &other) &&; + operator_sum operator+(const operator_sum &other) const &; + operator_sum operator+(const operator_sum &other) &&; + operator_sum operator-(const operator_sum &other) const &; + operator_sum operator-(const operator_sum &other) &&; operator_sum &operator*=(double other); operator_sum &operator+=(double other); From ae4ae7c286277aa3bca40b58d8098cd6992c1687 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Sat, 15 Feb 2025 21:50:50 +0000 Subject: [PATCH 276/311] Hook public API Signed-off-by: Thien Nguyen --- python/cudaq/operator/dynamics.yml | 2 + python/extension/CMakeLists.txt | 3 + runtime/cudaq/algorithms/evolve.h | 91 ++++++++++++++++++++++++++ runtime/cudaq/dynamics_integrators.h | 33 ++++++++++ unittests/CMakeLists.txt | 2 + unittests/dynamics/test_evolve_api.cpp | 49 ++++++++++++++ 6 files changed, 180 insertions(+) create mode 100644 runtime/cudaq/dynamics_integrators.h create mode 100644 unittests/dynamics/test_evolve_api.cpp diff --git a/python/cudaq/operator/dynamics.yml b/python/cudaq/operator/dynamics.yml index 537d00bc21..74276909ec 100644 --- a/python/cudaq/operator/dynamics.yml +++ b/python/cudaq/operator/dynamics.yml @@ -11,3 +11,5 @@ description: "Dynamics simulation backend" gpu-requirements: true config: nvqir-simulation-backend: dynamics + preprocessor-defines: ["-D CUDAQ_DYNAMICS_TARGET"] + diff --git a/python/extension/CMakeLists.txt b/python/extension/CMakeLists.txt index fe8431828e..6c245960c9 100644 --- a/python/extension/CMakeLists.txt +++ b/python/extension/CMakeLists.txt @@ -39,6 +39,9 @@ declare_mlir_dialect_python_bindings( dialects/cc.py DIALECT_NAME cc) +# FIXME: remove this flag +set (CMAKE_CXX_FLAGS "-fPIC ${CMAKE_CXX_FLAGS} -Wno-suggest-override") + declare_mlir_python_extension(CUDAQuantumPythonSources.Extension MODULE_NAME _quakeDialects ADD_TO_PARENT CUDAQuantumPythonSources diff --git a/runtime/cudaq/algorithms/evolve.h b/runtime/cudaq/algorithms/evolve.h index 38ee16e5a4..48af61d177 100644 --- a/runtime/cudaq/algorithms/evolve.h +++ b/runtime/cudaq/algorithms/evolve.h @@ -11,9 +11,13 @@ #include "common/EvolveResult.h" #include "common/KernelWrapper.h" #include "cudaq/algorithms/get_state.h" +#include "cudaq/base_integrator.h" +#include "cudaq/evolution.h" #include "cudaq/host_config.h" +#include "cudaq/operators.h" #include "cudaq/platform.h" #include "cudaq/platform/QuantumExecutionQueue.h" +#include "cudaq/schedule.h" namespace cudaq { @@ -152,4 +156,91 @@ evolve_async(std::function evolveFunctor, return f; } } // namespace __internal__ + +template , + cudaq::operator_sum> || + std::is_constructible_v< + cudaq::operator_sum, OperatorTy>>> +evolve_result evolve(const OperatorTy &hamiltonian, + const std::map &dimensions, + const Schedule &schedule, const state &initial_state, + std::shared_ptr integrator = {}, + const std::vector &collapse_operators = {}, + const std::vector &observables = {}, + bool store_intermediate_results = false, + std::optional shots_count = std::nullopt) { +#if defined(CUDAQ_DYNAMICS_TARGET) + if constexpr (std::is_same_v, + cudaq::operator_sum>) { + std::vector *> collapseOpsPtr; + for (const auto &cOp : collapse_operators) { + collapseOpsPtr.emplace_back( + const_cast *>(&cOp)); + } + std::vector *> observeOpsPtr; + for (const auto &obsOp : observables) { + observeOpsPtr.emplace_back( + const_cast *>(&obsOp)); + } + // FIXME: change signature of `evolve_single` so that we don't need to + // create the list of pointers. + return evolve_single(hamiltonian, dimensions, schedule, initial_state, + *integrator, collapseOpsPtr, observeOpsPtr, + store_intermediate_results); + } else { + std::vector> + convertedCollapseOps; + for (const auto &cOp : collapse_operators) { + convertedCollapseOps.emplace_back(cOp); + } + std::vector> + convertedObserveOps; + for (const auto &obsOp : observables) { + convertedObserveOps.emplace_back(obsOp); + } + std::vector *> collapseOpsPtr; + for (const auto &cOp : convertedCollapseOps) { + collapseOpsPtr.emplace_back( + const_cast *>(&cOp)); + } + std::vector *> observeOpsPtr; + for (const auto &obsOp : convertedObserveOps) { + observeOpsPtr.emplace_back( + const_cast *>(&obsOp)); + } + return evolve_single(hamiltonian, dimensions, schedule, initial_state, + *integrator, collapseOpsPtr, observeOpsPtr, + store_intermediate_results); + } +#else + throw std::runtime_error( + "cudaq::evolve is only supported on the 'dynamics' target. Please " + "recompile your application with '--target dynamics' flag."); +#endif +} +template , + cudaq::operator_sum> || + std::is_constructible_v< + cudaq::operator_sum, OperatorTy>>> +// Multiple input state +std::vector +evolve(const OperatorTy &hamiltonian, const std::map &dimensions, + const Schedule &schedule, const std::vector &initial_states, + std::shared_ptr integrator = {}, + const std::vector &collapse_operators = {}, + const std::vector &observables = {}, + bool store_intermediate_results = false, + std::optional shots_count = std::nullopt) { + std::vector results; + for (const auto &initial_state : initial_states) + results.emplace_back(evolve(hamiltonian, dimensions, schedule, + initial_states, integrator, collapse_operators, + observables, store_intermediate_results, + shots_count)); + return results; +} } // namespace cudaq diff --git a/runtime/cudaq/dynamics_integrators.h b/runtime/cudaq/dynamics_integrators.h new file mode 100644 index 0000000000..46accf9bee --- /dev/null +++ b/runtime/cudaq/dynamics_integrators.h @@ -0,0 +1,33 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/base_integrator.h" +#include + +namespace cudaq { +class runge_kutta : public BaseIntegrator { + +public: + std::optional order; + std::optional dt; + +public: + runge_kutta() = default; + // TODO + void integrate(double target_time) override {} + void set_state(cudaq::state initial_state, double t0) override {} + std::pair get_state() override { + return std::make_pair(m_t, cudaq::state(nullptr)); + } + +private: + double m_t; +}; +} // namespace cudaq diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 0970f6f276..2843ce5bcf 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -297,8 +297,10 @@ if (CUDA_FOUND) dynamics/test_cudm_time_stepper.cpp dynamics/test_cudm_expectation.cpp dynamics/test_evolve_single.cpp + dynamics/test_evolve_api.cpp ) add_executable(test_dynamics main.cpp ${CUDAQ_DYNAMICS_TEST_SOURCES}) + target_compile_definitions(test_dynamics PRIVATE -DCUDAQ_DYNAMICS_TARGET) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) target_link_options(test_dynamics PRIVATE -Wl,--no-as-needed) endif() diff --git a/unittests/dynamics/test_evolve_api.cpp b/unittests/dynamics/test_evolve_api.cpp new file mode 100644 index 0000000000..0fc4fbae41 --- /dev/null +++ b/unittests/dynamics/test_evolve_api.cpp @@ -0,0 +1,49 @@ +// /******************************************************************************* +// * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * +// * All rights reserved. * +// * * +// * This source code and the accompanying materials are made available under * +// * the terms of the Apache License 2.0 which accompanies this distribution. * +// ******************************************************************************/ + +#include "cudaq/algorithms/evolve.h" +#include "cudaq/dynamics_integrators.h" +#include +#include +#include + +TEST(EvolveAPITester, checkSimple) { + const std::map dims = {{0, 2}}; + cudaq::product_operator ham1 = + (2.0 * M_PI * 0.1 * cudaq::spin_operator::x(0)); + cudaq::operator_sum ham(ham1); + + constexpr int numSteps = 10; + cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); + + cudaq::product_operator pauliZ_t = + cudaq::spin_operator::z(0); + cudaq::operator_sum pauliZ(pauliZ_t); + auto initialState = + cudaq::state::from_data(std::vector>{1.0, 0.0}); + + auto integrator = std::make_shared(); + integrator->dt = 0.001; + integrator->order = 1; + auto result = cudaq::evolve(ham, dims, schedule, initialState, integrator, {}, + {pauliZ}, true); + // TODO: enable runge_kutta (fixing the dependency to cudm types) + // EXPECT_TRUE(result.get_expectation_values().has_value()); + // EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + // std::vector theoryResults; + // for (const auto &t : schedule) { + // const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); + // theoryResults.emplace_back(expected); + // } + + // int count = 0; + // for (auto expVals : result.get_expectation_values().value()) { + // EXPECT_EQ(expVals.size(), 1); + // EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + // } +} From 4bd04be7dd99299c481f6e1556446dee5743d438 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 17 Feb 2025 01:46:33 +0000 Subject: [PATCH 277/311] Port cudm_state under cudaq::SimulationState type Signed-off-by: Thien Nguyen --- runtime/cudaq/base_time_stepper.h | 11 + runtime/cudaq/dynamics_integrators.h | 22 +- runtime/cudaq/runge_kutta_integrator.h | 10 +- runtime/nvqir/cudensitymat/CMakeLists.txt | 3 +- .../nvqir/cudensitymat/CuDensityMatSim.cpp | 3 +- .../nvqir/cudensitymat/CuDensityMatState.cpp | 624 ++++++++++++++++++ .../nvqir/cudensitymat/CuDensityMatState.h | 245 +++---- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 65 +- runtime/nvqir/cudensitymat/cudm_solver.cpp | 2 +- runtime/nvqir/cudensitymat/cudm_state.cpp | 2 +- .../nvqir/cudensitymat/cudm_time_stepper.h | 2 +- .../cudensitymat/runge_kutta_integrator.cpp | 203 +++++- unittests/dynamics/test_cudm_expectation.cpp | 2 +- unittests/dynamics/test_cudm_helpers.cpp | 2 +- unittests/dynamics/test_cudm_state.cpp | 12 +- unittests/dynamics/test_cudm_time_stepper.cpp | 4 +- unittests/dynamics/test_evolve_api.cpp | 27 +- unittests/dynamics/test_evolve_single.cpp | 14 +- .../dynamics/test_runge_kutta_integrator.cpp | 3 +- 19 files changed, 1015 insertions(+), 241 deletions(-) create mode 100644 runtime/nvqir/cudensitymat/CuDensityMatState.cpp diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index 2c8150de0f..9a859b9ea1 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -7,6 +7,7 @@ ******************************************************************************/ #pragma once +#include "cudaq/qis/state.h" namespace cudaq { template @@ -21,4 +22,14 @@ class BaseTimeStepper { /// @return The updated quantum state after stepping. virtual TState compute(TState &state, double t, double step_size) = 0; }; + +class TimeStepper { +public: + virtual ~TimeStepper() = default; + + virtual state + compute(const state &inputState, double t, double step_size, + const std::unordered_map> + ¶meters) = 0; +}; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics_integrators.h b/runtime/cudaq/dynamics_integrators.h index 46accf9bee..fafc485cbc 100644 --- a/runtime/cudaq/dynamics_integrators.h +++ b/runtime/cudaq/dynamics_integrators.h @@ -9,9 +9,18 @@ #pragma once #include "cudaq/base_integrator.h" +#include "cudaq/base_time_stepper.h" +#include "cudaq/operators.h" #include namespace cudaq { +struct SystemDynamics { + operator_sum *hamiltonian = nullptr; + std::vector *> collapseOps; + std::vector modeExtents; + std::unordered_map> parameters; +}; + class runge_kutta : public BaseIntegrator { public: @@ -20,14 +29,15 @@ class runge_kutta : public BaseIntegrator { public: runge_kutta() = default; - // TODO - void integrate(double target_time) override {} - void set_state(cudaq::state initial_state, double t0) override {} - std::pair get_state() override { - return std::make_pair(m_t, cudaq::state(nullptr)); - } + void integrate(double target_time) override; + void set_state(cudaq::state initial_state, double t0) override; + std::pair get_state() override; + void set_system(const SystemDynamics &system); private: double m_t; + std::shared_ptr m_state; + SystemDynamics m_system; + std::unique_ptr m_stepper; }; } // namespace cudaq diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h index 6ff6d663b9..7549a76a75 100644 --- a/runtime/cudaq/runge_kutta_integrator.h +++ b/runtime/cudaq/runge_kutta_integrator.h @@ -12,7 +12,7 @@ #include namespace cudaq { -class cudm_state; +class CuDensityMatState; class cudm_time_stepper; class runge_kutta_integrator : public BaseIntegrator { @@ -22,19 +22,19 @@ class runge_kutta_integrator : public BaseIntegrator { public: runge_kutta_integrator() = default; - runge_kutta_integrator(cudm_state &&initial_state, double t0, + runge_kutta_integrator(CuDensityMatState &&initial_state, double t0, std::shared_ptr stepper, int substeps = 4); void integrate(double target_time) override; void set_state(cudaq::state initial_state, double t0) override; - void set_state(cudm_state &&initial_state); + void set_state(CuDensityMatState &&initial_state); void set_stepper(std::shared_ptr stepper); std::pair get_state() override; - std::pair get_cudm_state(); + std::pair get_cudm_state(); private: - std::unique_ptr m_state; + std::unique_ptr m_state; double m_t; std::shared_ptr m_stepper; }; diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 3192e41a96..5a3807c825 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -28,11 +28,12 @@ add_library(${LIBRARY_NAME} SHARED CuDensityMatSim.cpp mpi_support.cpp cudm_helpers.cpp - cudm_state.cpp + # cudm_state.cpp cudm_time_stepper.cpp runge_kutta_integrator.cpp cudm_expectation.cpp cudm_evolution.cpp + CuDensityMatState.cpp ) message("CUDAToolkit_INCLUDE_DIRS = ${CUDAToolkit_INCLUDE_DIRS}") diff --git a/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp b/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp index ba56d6fdbf..2ba56875c2 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp @@ -1,4 +1,4 @@ -/*************************************************************** -*- C++ -*- *** +/******************************************************************************* * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * @@ -10,6 +10,7 @@ #include "CuDensityMatState.h" #include "cudaq.h" #include "cudaq/distributed/mpi_plugin.h" +#include "cudm_error_handling.h" namespace { // Hook to query this shared lib file location at runtime. diff --git a/runtime/nvqir/cudensitymat/CuDensityMatState.cpp b/runtime/nvqir/cudensitymat/CuDensityMatState.cpp new file mode 100644 index 0000000000..6b926fd586 --- /dev/null +++ b/runtime/nvqir/cudensitymat/CuDensityMatState.cpp @@ -0,0 +1,624 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ +#include "CuDensityMatState.h" +#include "common/EigenDense.h" +#include "common/Logger.h" +#include "cudaq/utils/cudaq_utils.h" +#include "cudm_error_handling.h" + +namespace cudaq { + +std::complex +CuDensityMatState::overlap(const cudaq::SimulationState &other) { + if (getTensor().extents != other.getTensor().extents) + throw std::runtime_error("[CuDensityMatState] overlap error - other state " + "dimension not equal to this state dimension."); + + if (other.getPrecision() != getPrecision()) { + throw std::runtime_error( + "[CuDensityMatState] overlap error - precision mismatch."); + } + + if (!isDensityMatrix) { + Eigen::VectorXcd state(dimension); + const auto size = dimension; + HANDLE_CUDA_ERROR(cudaMemcpy(state.data(), devicePtr, + size * sizeof(std::complex), + cudaMemcpyDeviceToHost)); + + Eigen::VectorXcd otherState(dimension); + HANDLE_CUDA_ERROR(cudaMemcpy(otherState.data(), other.getTensor().data, + size * sizeof(std::complex), + cudaMemcpyDeviceToHost)); + return std::abs(std::inner_product( + state.begin(), state.end(), otherState.begin(), + std::complex{0., 0.}, [](auto a, auto b) { return a + b; }, + [](auto a, auto b) { return a * std::conj(b); })); + } + + // FIXME: implement this in GPU memory + Eigen::MatrixXcd state(dimension, dimension); + const auto size = dimension * dimension; + HANDLE_CUDA_ERROR(cudaMemcpy(state.data(), devicePtr, + size * sizeof(std::complex), + cudaMemcpyDeviceToHost)); + + Eigen::MatrixXcd otherState(dimension, dimension); + HANDLE_CUDA_ERROR(cudaMemcpy(otherState.data(), other.getTensor().data, + size * sizeof(std::complex), + cudaMemcpyDeviceToHost)); + + return (state.adjoint() * otherState).trace(); +} + +std::complex +CuDensityMatState::getAmplitude(const std::vector &basisState) { + throw std::runtime_error( + "[CuDensityMatState] getAmplitude by basis states is not supported. " + "Please use direct indexing access instead."); +} + +// Dump the state to the given output stream +void CuDensityMatState::dump(std::ostream &os) const { + // get state data from device to print + Eigen::MatrixXcd state(dimension, isDensityMatrix ? dimension : 1); + const auto size = isDensityMatrix ? dimension * dimension : dimension; + HANDLE_CUDA_ERROR(cudaMemcpy(state.data(), devicePtr, + size * sizeof(std::complex), + cudaMemcpyDeviceToHost)); + os << state << std::endl; +} + +std::unique_ptr +CuDensityMatState::createFromSizeAndPtr(std::size_t size, void *dataPtr, + std::size_t type) { + bool isDm = false; + if (type == cudaq::detail::variant_index()) { + if (size != 1) + throw std::runtime_error("[CuDensityMatState]: createFromSizeAndPtr " + "expects a single tensor"); + auto *casted = + reinterpret_cast(dataPtr); + + auto [ptr, extents] = casted[0]; + if (extents.size() > 2) + throw std::runtime_error("[CuDensityMatState]: createFromSizeAndPtr only " + "accept 1D or 2D arrays"); + + isDm = extents.size() == 2; + size = std::reduce(extents.begin(), extents.end(), 1, std::multiplies()); + dataPtr = const_cast(ptr); + } + + std::complex *devicePtr = nullptr; + + HANDLE_CUDA_ERROR( + cudaMalloc((void **)&devicePtr, size * sizeof(std::complex))); + HANDLE_CUDA_ERROR(cudaMemcpy(devicePtr, dataPtr, + size * sizeof(std::complex), + cudaMemcpyDefault)); + // printf("Created CuDensityMatState ptr %p\n", devicePtr); + return std::make_unique(size, devicePtr, isDm); +} + +// Return the tensor at the given index. Throws +// for an invalid tensor index. +cudaq::SimulationState::Tensor +CuDensityMatState::getTensor(std::size_t tensorIdx) const { + if (tensorIdx != 0) { + throw std::runtime_error( + "CuDensityMatState state only supports a single tensor"); + } + const std::vector extents = + isDensityMatrix ? std::vector{dimension, dimension} + : std::vector{dimension}; + return Tensor{devicePtr, extents, precision::fp64}; +} + +std::complex +CuDensityMatState::operator()(std::size_t tensorIdx, + const std::vector &indices) { + const auto extractValue = [&](std::size_t idx) { + std::complex value; + HANDLE_CUDA_ERROR(cudaMemcpy( + &value, reinterpret_cast *>(devicePtr) + idx, + sizeof(std::complex), cudaMemcpyDeviceToHost)); + return value; + }; + + if (tensorIdx != 0) + throw std::runtime_error( + "CuDensityMatState state only supports a single tensor"); + if (isDensityMatrix) { + if (indices.size() != 2) + throw std::runtime_error("CuDensityMatState holding a density matrix " + "supports only 2-dimensional indices"); + if (indices[0] >= dimension || indices[1] >= dimension) + throw std::runtime_error("CuDensityMatState indices out of range"); + return extractValue(indices[0] * dimension + indices[1]); + } + if (indices.size() != 1) + throw std::runtime_error( + "CuDensityMatState holding a state vector supports " + "only 1-dimensional indices"); + if (indices[0] >= dimension) + throw std::runtime_error("Index out of bounds"); + return extractValue(indices[0]); +} + +// Copy the state device data to the user-provided host data pointer. +void CuDensityMatState::toHost(std::complex *userData, + std::size_t numElements) const { + if (numElements != dimension * (isDensityMatrix ? dimension : 1)) { + throw std::runtime_error("Number of elements in user data does not match " + "the size of the state"); + } + HANDLE_CUDA_ERROR(cudaMemcpy(userData, devicePtr, + numElements * sizeof(std::complex), + cudaMemcpyDeviceToHost)); +} + +// Copy the state device data to the user-provided host data pointer. +void CuDensityMatState::toHost(std::complex *userData, + std::size_t numElements) const { + throw std::runtime_error( + "CuDensityMatState: Data type mismatches - expecting " + "double-precision array."); +} + +// Free the device data. +void CuDensityMatState::destroyState() { + if (cudmState) { + cudensitymatDestroyState(cudmState); + cudmState = nullptr; + } + if (devicePtr != nullptr) { + HANDLE_CUDA_ERROR(cudaFree(devicePtr)); + devicePtr = nullptr; + dimension = 0; + isDensityMatrix = false; + } +} + +static size_t +calculate_state_vector_size(const std::vector &hilbertSpaceDims) { + return std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), 1, + std::multiplies<>()); +} + +static size_t +calculate_density_matrix_size(const std::vector &hilbertSpaceDims) { + size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); + return vectorSize * vectorSize; +} + +CuDensityMatState::CuDensityMatState( + cudensitymatHandle_t handle, + const std::vector> &rawData, + const std::vector &dims) + : cudmHandle(handle), dimension(rawData.size()), cudmState(nullptr), + hilbertSpaceDims(dims) { + if (rawData.empty()) { + throw std::invalid_argument("Raw data cannot be empty."); + } + // Allocate device memory + size_t dataSize = rawData.size() * sizeof(std::complex); + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&devicePtr), dataSize)); + + // Copy data from host to device + HANDLE_CUDA_ERROR( + cudaMemcpy(devicePtr, rawData.data(), dataSize, cudaMemcpyHostToDevice)); + + // Determine if this is a denisty matrix or state vector + size_t rawDataSize = rawData.size(); + size_t expectedDensityMatrixSize = + calculate_density_matrix_size(hilbertSpaceDims); + size_t expectedStateVectorSize = + calculate_state_vector_size(hilbertSpaceDims); + + if (rawDataSize != expectedDensityMatrixSize && + rawDataSize != expectedStateVectorSize) { + throw std::invalid_argument( + "Invalid rawData size for the given Hilbert space dimensions."); + } + + cudensitymatStatePurity_t purity; + + if (rawDataSize == expectedDensityMatrixSize) { + purity = CUDENSITYMAT_STATE_PURITY_MIXED; + } else if (rawDataSize == expectedStateVectorSize) { + purity = CUDENSITYMAT_STATE_PURITY_PURE; + } + + HANDLE_CUDM_ERROR(cudensitymatCreateState( + cudmHandle, purity, static_cast(hilbertSpaceDims.size()), + hilbertSpaceDims.data(), 1, CUDA_C_64F, &cudmState)); + + // Retrieve the number of state components + int32_t numStateComponents; + HANDLE_CUDM_ERROR(cudensitymatStateGetNumComponents(cudmHandle, cudmState, + &numStateComponents)); + + // Retrieve the storage size for each component + std::vector componentBufferSizes(numStateComponents); + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + cudmHandle, cudmState, numStateComponents, componentBufferSizes.data())); + + // Validate device memory + size_t totalSize = std::accumulate(componentBufferSizes.begin(), + componentBufferSizes.end(), 0); + if (totalSize > rawData.size() * sizeof(std::complex)) { + throw std::invalid_argument( + "Device memory size is insufficient to cover all components."); + } + + // Attach storage for using device memory (devicePtr) + std::vector componentBuffers(numStateComponents); + size_t offset = 0; + for (int32_t i = 0; i < numStateComponents; i++) { + componentBuffers[i] = static_cast( + static_cast *>(devicePtr) + offset); + offset += componentBufferSizes[i] / sizeof(std::complex); + } + + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + cudmHandle, cudmState, numStateComponents, componentBuffers.data(), + componentBufferSizes.data())); +} + +CuDensityMatState::CuDensityMatState(cudensitymatHandle_t handle, + const CuDensityMatState &simState, + const std::vector &dims) + : cudmHandle(handle), hilbertSpaceDims(dims) { + + const bool isDensityMat = + simState.dimension == calculate_density_matrix_size(hilbertSpaceDims); + dimension = simState.dimension; + + const size_t dataSize = dimension * sizeof(std::complex); + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&devicePtr), dataSize)); + + HANDLE_CUDA_ERROR( + cudaMemcpy(devicePtr, simState.devicePtr, dataSize, cudaMemcpyDefault)); + + const cudensitymatStatePurity_t purity = isDensityMat + ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + HANDLE_CUDM_ERROR(cudensitymatCreateState( + cudmHandle, purity, static_cast(hilbertSpaceDims.size()), + hilbertSpaceDims.data(), 1, CUDA_C_64F, &cudmState)); + + // Query the size of the quantum state storage + std::size_t storageSize{0}; // only one storage component (tensor) is needed + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + cudmHandle, cudmState, + 1, // only one storage component + &storageSize)); // storage size in bytes + const std::size_t stateVolume = + storageSize / sizeof(std::complex); // quantum state tensor volume + // (number of elements) + assert(stateVolume == dimension); + // std::cout << "Quantum state storage size (bytes) = " << storageSize + // << std::endl; + + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + cudmHandle, cudmState, + 1, // only one storage component (tensor) + std::vector({devicePtr}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state +} + +CuDensityMatState CuDensityMatState::zero_like(const CuDensityMatState &other) { + CuDensityMatState state; + state.cudmHandle = other.cudmHandle; + state.hilbertSpaceDims = other.hilbertSpaceDims; + state.dimension = other.dimension; + const size_t dataSize = state.dimension * sizeof(std::complex); + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&state.devicePtr), dataSize)); + HANDLE_CUDA_ERROR(cudaMemset(state.devicePtr, 0, dataSize)); + + const size_t expectedDensityMatrixSize = + calculate_density_matrix_size(state.hilbertSpaceDims); + const bool isDensityMat = expectedDensityMatrixSize == state.dimension; + const cudensitymatStatePurity_t purity = isDensityMat + ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + HANDLE_CUDM_ERROR(cudensitymatCreateState( + state.cudmHandle, purity, + static_cast(state.hilbertSpaceDims.size()), + state.hilbertSpaceDims.data(), 1, CUDA_C_64F, &state.cudmState)); + + // Query the size of the quantum state storage + std::size_t storageSize{0}; // only one storage component (tensor) is needed + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + state.cudmHandle, state.cudmState, + 1, // only one storage component + &storageSize)); // storage size in bytes + const std::size_t stateVolume = + storageSize / sizeof(std::complex); // quantum state tensor volume + // (number of elements) + assert(stateVolume == state.dimension); + // std::cout << "Quantum state storage size (bytes) = " << storageSize + // << std::endl; + + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + state.cudmHandle, state.cudmState, + 1, // only one storage component (tensor) + std::vector({state.devicePtr}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state + return state; +} + +CuDensityMatState CuDensityMatState::clone(const CuDensityMatState &other) { + CuDensityMatState state; + state.cudmHandle = other.cudmHandle; + state.hilbertSpaceDims = other.hilbertSpaceDims; + state.dimension = other.dimension; + const size_t dataSize = state.dimension * sizeof(std::complex); + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&state.devicePtr), dataSize)); + HANDLE_CUDA_ERROR(cudaMemcpy(state.devicePtr, other.devicePtr, dataSize, + cudaMemcpyDefault)); + + const size_t expectedDensityMatrixSize = + calculate_density_matrix_size(state.hilbertSpaceDims); + const bool isDensityMat = expectedDensityMatrixSize == state.dimension; + const cudensitymatStatePurity_t purity = isDensityMat + ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + HANDLE_CUDM_ERROR(cudensitymatCreateState( + state.cudmHandle, purity, + static_cast(state.hilbertSpaceDims.size()), + state.hilbertSpaceDims.data(), 1, CUDA_C_64F, &state.cudmState)); + + // Query the size of the quantum state storage + std::size_t storageSize{0}; // only one storage component (tensor) is needed + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + state.cudmHandle, state.cudmState, + 1, // only one storage component + &storageSize)); // storage size in bytes + const std::size_t stateVolume = + storageSize / sizeof(std::complex); // quantum state tensor volume + // (number of elements) + assert(stateVolume == state.dimension); + // std::cout << "Quantum state storage size (bytes) = " << storageSize + // << std::endl; + + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + state.cudmHandle, state.cudmState, + 1, // only one storage component (tensor) + std::vector({state.devicePtr}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state + return state; +} + +CuDensityMatState::CuDensityMatState(CuDensityMatState &&other) noexcept + : isDensityMatrix(other.isDensityMatrix), dimension(other.dimension), + devicePtr(other.devicePtr), cudmState(other.cudmState), + cudmHandle(other.cudmHandle), hilbertSpaceDims(other.hilbertSpaceDims) { + other.isDensityMatrix = false; + other.dimension = 0; + other.devicePtr = nullptr; + + other.cudmState = nullptr; + other.cudmHandle = nullptr; + other.hilbertSpaceDims.clear(); +} + +CuDensityMatState & +CuDensityMatState::operator=(CuDensityMatState &&other) noexcept { + if (this != &other) { + // Free existing resources + if (cudmState) { + cudensitymatDestroyState(cudmState); + } + if (devicePtr) { + cudaFree(devicePtr); + } + + // Move data from other + isDensityMatrix = other.isDensityMatrix; + dimension = other.dimension; + devicePtr = other.devicePtr; + cudmState = other.cudmState; + cudmHandle = other.cudmHandle; + hilbertSpaceDims = std::move(other.hilbertSpaceDims); + + // Nullify other + other.isDensityMatrix = false; + other.dimension = 0; + other.devicePtr = nullptr; + + other.cudmState = nullptr; + } + return *this; +} + +CuDensityMatState::~CuDensityMatState() { destroyState(); } + +bool CuDensityMatState::is_initialized() const { return cudmState != nullptr; } + +bool cudaq::CuDensityMatState::is_density_matrix() const { + if (!is_initialized()) { + return false; + } + + return dimension == calculate_density_matrix_size(hilbertSpaceDims); +} + +CuDensityMatState cudaq::CuDensityMatState::to_density_matrix() const { + if (!is_initialized()) { + throw std::runtime_error("State is not initialized."); + } + + if (is_density_matrix()) { + throw std::runtime_error("State is already a density matrix."); + } + + size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); + std::vector> stateVecData(vectorSize); + HANDLE_CUDA_ERROR(cudaMemcpy(stateVecData.data(), devicePtr, + dimension * sizeof(std::complex), + cudaMemcpyDeviceToHost)); + size_t expectedDensityMatrixSize = vectorSize * vectorSize; + std::vector> densityMatrix(expectedDensityMatrixSize); + + for (size_t i = 0; i < vectorSize; i++) { + for (size_t j = 0; j < vectorSize; j++) { + densityMatrix[i * vectorSize + j] = + stateVecData[i] * std::conj(stateVecData[j]); + } + } + + return CuDensityMatState(cudmHandle, densityMatrix, hilbertSpaceDims); +} + +cudensitymatState_t cudaq::CuDensityMatState::get_impl() const { + return cudmState; +} + +void *cudaq::CuDensityMatState::get_device_pointer() const { return devicePtr; } + +std::vector cudaq::CuDensityMatState::get_hilbert_space_dims() const { + return hilbertSpaceDims; +} + +cudensitymatHandle_t cudaq::CuDensityMatState::get_handle() const { + return cudmHandle; +} + +void CuDensityMatState::initialize_cudm( + cudensitymatHandle_t handleToSet, + const std::vector &dims) { + cudmHandle = handleToSet; + hilbertSpaceDims = dims; + size_t expectedDensityMatrixSize = + calculate_density_matrix_size(hilbertSpaceDims); + size_t expectedStateVectorSize = + calculate_state_vector_size(hilbertSpaceDims); + + if (dimension != expectedDensityMatrixSize && + dimension != expectedStateVectorSize) { + throw std::invalid_argument("Invalid hilbertSpaceDims for the state data"); + } + + const cudensitymatStatePurity_t purity = + dimension == expectedDensityMatrixSize ? CUDENSITYMAT_STATE_PURITY_MIXED + : CUDENSITYMAT_STATE_PURITY_PURE; + + HANDLE_CUDM_ERROR(cudensitymatCreateState( + cudmHandle, purity, static_cast(hilbertSpaceDims.size()), + hilbertSpaceDims.data(), 1, CUDA_C_64F, &cudmState)); + + std::size_t storageSize; + HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( + cudmHandle, cudmState, + 1, // only one storage component + &storageSize)); // storage size in bytes + // Attach initialized GPU storage to the input quantum state + HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( + cudmHandle, cudmState, + 1, // only one storage component (tensor) + std::vector({devicePtr}) + .data(), // pointer to the GPU storage for the quantum state + std::vector({storageSize}) + .data())); // size of the GPU storage for the quantum state +} + +CuDensityMatState +cudaq::CuDensityMatState::operator+(const CuDensityMatState &other) const { + if (dimension != other.dimension) { + throw std::invalid_argument("State size mismatch for addition."); + } + + CuDensityMatState result = CuDensityMatState::clone(*this); + + double scalingFactor = 1.0; + double *gpuScalingFactor; + HANDLE_CUDA_ERROR( + cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double))); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), + cudaMemcpyHostToDevice)); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( + cudmHandle, other.get_impl(), result.get_impl(), gpuScalingFactor, 0)); + + HANDLE_CUDA_ERROR(cudaFree(gpuScalingFactor)); + + return result; +} + +CuDensityMatState & +cudaq::CuDensityMatState::operator+=(const CuDensityMatState &other) { + if (dimension != other.dimension) { + throw std::invalid_argument( + fmt::format("State size mismatch for addition ({} vs {}).", dimension, + other.dimension)); + } + + double scalingFactor = 1.0; + double *gpuScalingFactor; + cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); + cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), + cudaMemcpyHostToDevice); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( + cudmHandle, other.get_impl(), cudmState, gpuScalingFactor, 0)); + + cudaFree(gpuScalingFactor); + + return *this; +} + +CuDensityMatState & +cudaq::CuDensityMatState::operator*=(const std::complex &scalar) { + void *gpuScalar; + HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &scalar, sizeof(std::complex), + cudaMemcpyHostToDevice)); + + HANDLE_CUDM_ERROR( + cudensitymatStateComputeScaling(cudmHandle, cudmState, gpuScalar, 0)); + + HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); + + return *this; +} + +CuDensityMatState cudaq::CuDensityMatState::operator*(double scalar) const { + void *gpuScalar; + HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); + + std::complex complexScalar(scalar, 0.0); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &complexScalar, + sizeof(std::complex), + cudaMemcpyHostToDevice)); + + CuDensityMatState result = CuDensityMatState::clone(*this); + + HANDLE_CUDM_ERROR(cudensitymatStateComputeScaling( + cudmHandle, result.cudmState, gpuScalar, 0)); + + HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); + + return result; +} +} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/CuDensityMatState.h b/runtime/nvqir/cudensitymat/CuDensityMatState.h index 5c0b195d4d..f11a20d54f 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatState.h +++ b/runtime/nvqir/cudensitymat/CuDensityMatState.h @@ -7,21 +7,8 @@ ******************************************************************************/ #pragma once -#include "common/EigenDense.h" -#include "common/Logger.h" #include "common/SimulationState.h" -#include "cudaq/utils/cudaq_utils.h" -#include - -#define HANDLE_CUDA_ERROR(x) \ - { \ - const auto err = x; \ - if (err != cudaSuccess) { \ - throw std::runtime_error( \ - fmt::format("[CuDensityMatState] %{} in {} (line {})", \ - cudaGetErrorString(err), __FUNCTION__, __LINE__)); \ - } \ - }; +#include namespace cudaq { /// @cond @@ -34,6 +21,10 @@ class CuDensityMatState : public cudaq::SimulationState { // State device data pointer. void *devicePtr = nullptr; + cudensitymatState_t cudmState = nullptr; + cudensitymatHandle_t cudmHandle = nullptr; + std::vector hilbertSpaceDims; + public: CuDensityMatState(std::size_t s, void *ptr, bool isDm) : isDensityMatrix(isDm), devicePtr(ptr), @@ -43,66 +34,13 @@ class CuDensityMatState : public cudaq::SimulationState { std::size_t getNumQubits() const override { return std::log2(dimension); } - std::complex overlap(const cudaq::SimulationState &other) override { - if (getTensor().extents != other.getTensor().extents) - throw std::runtime_error( - "[CuDensityMatState] overlap error - other state " - "dimension not equal to this state dimension."); - - if (other.getPrecision() != getPrecision()) { - throw std::runtime_error( - "[CuDensityMatState] overlap error - precision mismatch."); - } - - if (!isDensityMatrix) { - Eigen::VectorXcd state(dimension); - const auto size = dimension; - HANDLE_CUDA_ERROR(cudaMemcpy(state.data(), devicePtr, - size * sizeof(std::complex), - cudaMemcpyDeviceToHost)); - - Eigen::VectorXcd otherState(dimension); - HANDLE_CUDA_ERROR(cudaMemcpy(otherState.data(), other.getTensor().data, - size * sizeof(std::complex), - cudaMemcpyDeviceToHost)); - return std::abs(std::inner_product( - state.begin(), state.end(), otherState.begin(), - std::complex{0., 0.}, [](auto a, auto b) { return a + b; }, - [](auto a, auto b) { return a * std::conj(b); })); - } - - // FIXME: implement this in GPU memory - Eigen::MatrixXcd state(dimension, dimension); - const auto size = dimension * dimension; - HANDLE_CUDA_ERROR(cudaMemcpy(state.data(), devicePtr, - size * sizeof(std::complex), - cudaMemcpyDeviceToHost)); - - Eigen::MatrixXcd otherState(dimension, dimension); - HANDLE_CUDA_ERROR(cudaMemcpy(otherState.data(), other.getTensor().data, - size * sizeof(std::complex), - cudaMemcpyDeviceToHost)); - - return (state.adjoint() * otherState).trace(); - } + std::complex overlap(const cudaq::SimulationState &other) override; std::complex - getAmplitude(const std::vector &basisState) override { - throw std::runtime_error( - "[CuDensityMatState] getAmplitude by basis states is not supported. " - "Please use direct indexing access instead."); - } + getAmplitude(const std::vector &basisState) override; // Dump the state to the given output stream - void dump(std::ostream &os) const override { - // get state data from device to print - Eigen::MatrixXcd state(dimension, isDensityMatrix ? dimension : 1); - const auto size = isDensityMatrix ? dimension * dimension : dimension; - HANDLE_CUDA_ERROR(cudaMemcpy(state.data(), devicePtr, - size * sizeof(std::complex), - cudaMemcpyDeviceToHost)); - os << state << std::endl; - } + void dump(std::ostream &os) const override; // This state is GPU device data, always return true. bool isDeviceData() const override { return true; } @@ -116,50 +54,11 @@ class CuDensityMatState : public cudaq::SimulationState { std::unique_ptr createFromSizeAndPtr(std::size_t size, void *dataPtr, - std::size_t type) override { - bool isDm = false; - if (type == cudaq::detail::variant_index()) { - if (size != 1) - throw std::runtime_error("[CuDensityMatState]: createFromSizeAndPtr " - "expects a single tensor"); - auto *casted = - reinterpret_cast(dataPtr); - - auto [ptr, extents] = casted[0]; - if (extents.size() > 2) - throw std::runtime_error( - "[CuDensityMatState]: createFromSizeAndPtr only " - "accept 1D or 2D arrays"); - - isDm = extents.size() == 2; - size = std::reduce(extents.begin(), extents.end(), 1, std::multiplies()); - dataPtr = const_cast(ptr); - } - - std::complex *devicePtr = nullptr; - - HANDLE_CUDA_ERROR( - cudaMalloc((void **)&devicePtr, size * sizeof(std::complex))); - HANDLE_CUDA_ERROR(cudaMemcpy(devicePtr, dataPtr, - size * sizeof(std::complex), - cudaMemcpyDefault)); - // printf("Created CuDensityMatState ptr %p\n", devicePtr); - return std::make_unique(size, devicePtr, isDm); - } + std::size_t type) override; // Return the tensor at the given index. Throws // for an invalid tensor index. - Tensor getTensor(std::size_t tensorIdx = 0) const override { - if (tensorIdx != 0) { - throw std::runtime_error( - "CuDensityMatState state only supports a single tensor"); - } - const std::vector extents = - isDensityMatrix ? std::vector{dimension, dimension} - : std::vector{dimension}; - return Tensor{devicePtr, extents, precision::fp64}; - } + Tensor getTensor(std::size_t tensorIdx = 0) const override; // Return all tensors that represent this state std::vector getTensors() const override { return {getTensor()}; } @@ -169,64 +68,86 @@ class CuDensityMatState : public cudaq::SimulationState { std::complex operator()(std::size_t tensorIdx, - const std::vector &indices) override { - const auto extractValue = [&](std::size_t idx) { - std::complex value; - HANDLE_CUDA_ERROR(cudaMemcpy( - &value, reinterpret_cast *>(devicePtr) + idx, - sizeof(std::complex), cudaMemcpyDeviceToHost)); - return value; - }; - - if (tensorIdx != 0) - throw std::runtime_error( - "CuDensityMatState state only supports a single tensor"); - if (isDensityMatrix) { - if (indices.size() != 2) - throw std::runtime_error("CuDensityMatState holding a density matrix " - "supports only 2-dimensional indices"); - if (indices[0] >= dimension || indices[1] >= dimension) - throw std::runtime_error("CuDensityMatState indices out of range"); - return extractValue(indices[0] * dimension + indices[1]); - } - if (indices.size() != 1) - throw std::runtime_error( - "CuDensityMatState holding a state vector supports " - "only 1-dimensional indices"); - if (indices[0] >= dimension) - throw std::runtime_error("Index out of bounds"); - return extractValue(indices[0]); - } + const std::vector &indices) override; // Copy the state device data to the user-provided host data pointer. void toHost(std::complex *userData, - std::size_t numElements) const override { - if (numElements != dimension * (isDensityMatrix ? dimension : 1)) { - throw std::runtime_error("Number of elements in user data does not match " - "the size of the state"); - } - HANDLE_CUDA_ERROR(cudaMemcpy(userData, devicePtr, - numElements * sizeof(std::complex), - cudaMemcpyDeviceToHost)); - } + std::size_t numElements) const override; // Copy the state device data to the user-provided host data pointer. void toHost(std::complex *userData, - std::size_t numElements) const override { - throw std::runtime_error( - "CuDensityMatState: Data type mismatches - expecting " - "double-precision array."); - } - + std::size_t numElements) const override; // Free the device data. - void destroyState() override { - if (devicePtr != nullptr) { - HANDLE_CUDA_ERROR(cudaFree(devicePtr)); - devicePtr = nullptr; - dimension = 0; - isDensityMatrix = false; - } - } + void destroyState() override; + + // TODO: Tidy this up, remove unnecessary methods + /// @brief To initialize state with raw data. + explicit CuDensityMatState(cudensitymatHandle_t handle, + const std::vector> &rawData, + const std::vector &hilbertSpaceDims); + /// @brief To initialize state from a `cudaq::state` + explicit CuDensityMatState(cudensitymatHandle_t handle, + const CuDensityMatState &simState, + const std::vector &hilbertSpaceDims); + // @brief Create a zero state + static CuDensityMatState zero_like(const CuDensityMatState &other); + static CuDensityMatState clone(const CuDensityMatState &other); + // Prevent copies (avoids double free issues) + CuDensityMatState(const CuDensityMatState &) = delete; + CuDensityMatState &operator=(const CuDensityMatState &) = delete; + + // Allow move semantics + CuDensityMatState(CuDensityMatState &&other) noexcept; + CuDensityMatState &operator=(CuDensityMatState &&other) noexcept; + + /// @brief Destructor to clean up resources + ~CuDensityMatState(); + + /// @brief Check if the state is initialized. + /// @return True if the state is initialized, false otherwise. + bool is_initialized() const; + + /// @brief Check if the state is a density matrix. + /// @return True if the state is a density matrix, false otherwise. + bool is_density_matrix() const; + + /// @brief Convert the state vector to a density matrix. + /// @return A new CuDensityMatState representing the density matrix. + CuDensityMatState to_density_matrix() const; + + /// @brief Get the underlying implementation (if any). + /// @return The underlying state implementation. + cudensitymatState_t get_impl() const; + + /// @brief Get the pointer to device memory buffer storing the state. + /// @return GPU device pointer + void *get_device_pointer() const; + + /// @brief Get a copy of the hilbert space dimensions for the quantum state. + /// @return A copy of the hilbert space dimensions of a vector of integers. + std::vector get_hilbert_space_dims() const; + + /// @brief Returns the handle + /// @return The handle associated with the state + cudensitymatHandle_t get_handle() const; + + void initialize_cudm(cudensitymatHandle_t handleToSet, + const std::vector &hilbertSpaceDims); + /// @brief Addition operator (element-wise) + /// @return The new state after the summation of two states. + CuDensityMatState operator+(const CuDensityMatState &other) const; + + /// @brief Accumulation operator + /// @return Accumulates the summation of two states. + CuDensityMatState &operator+=(const CuDensityMatState &other); + + /// @brief Scalar multiplication operator + /// @return The new state after multiplying scalar with the current state. + CuDensityMatState &operator*=(const std::complex &scalar); + + CuDensityMatState operator*(double scalar) const; }; /// @endcond + +typedef CuDensityMatState cudm_state; } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 04fc77be97..5989aacec4 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -1,4 +1,4 @@ -/****************************************************************-*- C++ -*-**** +/******************************************************************************* * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * @@ -6,12 +6,12 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatState.h" +#include "cudaq/dynamics_integrators.h" #include "cudaq/evolution.h" -#include "cudaq/runge_kutta_integrator.h" #include "cudm_error_handling.h" #include "cudm_expectation.h" #include "cudm_helpers.h" -#include "cudm_state.h" #include "cudm_time_stepper.h" #include #include @@ -28,27 +28,30 @@ evolve_result evolve_single( bool store_intermediate_results, std::optional shots_count) { cudensitymatHandle_t handle; HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); - - cudm_helper helper(handle); - std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); + const auto asCudmState = [](cudaq::state &cudaqState) -> CuDensityMatState * { + auto *simState = cudaq::state_helper::getSimulationState(&cudaqState); + auto *castSimState = dynamic_cast(simState); + if (!castSimState) + throw std::runtime_error("Invalid state."); + return castSimState; + }; + asCudmState(const_cast(initial_state)) + ->initialize_cudm(handle, dims); - auto cudmState = cudm_state(handle, initial_state, dims); - auto liouvillian = helper.construct_liouvillian( - hamiltonian, collapse_operators, dims, {}, cudmState.is_density_matrix()); - // std::cout << "Evolve Liouvillian: " << liouvillian << "\n"; - // Need to pass liouvillian here - auto time_stepper = std::make_shared(handle, liouvillian); - runge_kutta_integrator &integrator = - dynamic_cast(in_integrator); - integrator.set_stepper(time_stepper); - integrator.set_state(std::move(cudmState)); - // auto integrator = std::make_unique( - // cudm_state(handle, initial_state, dims), 0.0, time_stepper, 1); - // integrator.set_option("dt", 0.000001); + runge_kutta &integrator = dynamic_cast(in_integrator); + SystemDynamics system; + system.hamiltonian = + const_cast *>(&hamiltonian); + system.collapseOps = collapse_operators; + system.modeExtents = dims; + integrator.set_system(system); + integrator.set_state(initial_state, 0.0); + + cudm_helper helper(handle); std::vector expectations; for (auto &obs : observables) expectations.emplace_back(cudm_expectation( @@ -56,35 +59,37 @@ evolve_result evolve_single( {}, *obs, dims))); std::vector> expectationVals; + std::vector intermediateStates; for (const auto &step : schedule) { integrator.integrate(step); - auto [t, currentState] = integrator.get_cudm_state(); + auto [t, currentState] = integrator.get_state(); if (store_intermediate_results) { std::vector expVals; + for (auto &expectation : expectations) { - expectation.prepare(currentState->get_impl()); - const auto expVal = expectation.compute(currentState->get_impl(), step); + auto *cudmState = asCudmState(currentState); + expectation.prepare(cudmState->get_impl()); + const auto expVal = expectation.compute(cudmState->get_impl(), step); expVals.emplace_back(expVal.real()); } expectationVals.emplace_back(std::move(expVals)); + intermediateStates.emplace_back(currentState); } } if (store_intermediate_results) { - // TODO: need to convert to proper state - return evolve_result({initial_state}, expectationVals); + return evolve_result(intermediateStates, expectationVals); } else { // Only final state is needed - auto [finalTime, finalState] = integrator.get_cudm_state(); + auto [finalTime, finalState] = integrator.get_state(); std::vector expVals; + auto *cudmState = asCudmState(finalState); for (auto &expectation : expectations) { - expectation.prepare(finalState->get_impl()); - const auto expVal = - expectation.compute(finalState->get_impl(), finalTime); + expectation.prepare(cudmState->get_impl()); + const auto expVal = expectation.compute(cudmState->get_impl(), finalTime); expVals.emplace_back(expVal.real()); } - // TODO: need to convert to proper state - return evolve_result(initial_state, expVals); + return evolve_result(finalState, expVals); } } diff --git a/runtime/nvqir/cudensitymat/cudm_solver.cpp b/runtime/nvqir/cudensitymat/cudm_solver.cpp index 2da0ac54a6..dd401bac1f 100644 --- a/runtime/nvqir/cudensitymat/cudm_solver.cpp +++ b/runtime/nvqir/cudensitymat/cudm_solver.cpp @@ -8,7 +8,7 @@ #include "cudm_solver.h" #include "cudm_helpers.h" -#include "cudm_state.h" +#include "CuDensityMatState.h" #include "cudm_time_stepper.h" namespace cudaq { diff --git a/runtime/nvqir/cudensitymat/cudm_state.cpp b/runtime/nvqir/cudensitymat/cudm_state.cpp index cd17daa90c..7787c3a07e 100644 --- a/runtime/nvqir/cudensitymat/cudm_state.cpp +++ b/runtime/nvqir/cudensitymat/cudm_state.cpp @@ -6,7 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudm_state.h" +#include "CuDensityMatState.h" #include "cudm_error_handling.h" #include #include diff --git a/runtime/nvqir/cudensitymat/cudm_time_stepper.h b/runtime/nvqir/cudensitymat/cudm_time_stepper.h index 44acac71a7..5de78ccd76 100644 --- a/runtime/nvqir/cudensitymat/cudm_time_stepper.h +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.h @@ -9,7 +9,7 @@ #pragma once #include "cudaq/base_time_stepper.h" -#include "cudm_state.h" +#include "CuDensityMatState.h" #include namespace cudaq { diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index b3697b9155..c8eec0d4ee 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -7,9 +7,210 @@ ******************************************************************************/ #include "cudaq/runge_kutta_integrator.h" -#include "cudm_state.h" +#include "CuDensityMatState.h" +#include "cudaq/dynamics_integrators.h" +#include "cudm_error_handling.h" +#include "cudm_helpers.h" #include "cudm_time_stepper.h" +namespace { +using namespace cudaq; +class cudmStepper : public TimeStepper { +public: + explicit cudmStepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian) + : m_handle(handle), m_liouvillian(liouvillian){}; + + state compute(const state &inputState, double t, double step_size, + const std::unordered_map> + ¶meters) override { + if (step_size == 0.0) { + throw std::runtime_error("Step size cannot be zero."); + } + + auto *simState = cudaq::state_helper::getSimulationState( + const_cast(&inputState)); + auto *castSimState = dynamic_cast(simState); + if (!castSimState) + throw std::runtime_error("Invalid state."); + CuDensityMatState &state = *castSimState; + // Prepare workspace + cudensitymatWorkspaceDescriptor_t workspace; + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(m_handle, &workspace)); + + // Query free gpu memory and allocate workspace buffer + std::size_t freeMem = 0, totalMem = 0; + HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); + // Take 80% of free memory + freeMem = static_cast(static_cast(freeMem) * 0.80); + + // Create a new state for the next step + auto next_state = CuDensityMatState::zero_like(state); + + if (!next_state.is_initialized()) { + throw std::runtime_error("Next state failed to initialize."); + } + + if (state.get_hilbert_space_dims() != next_state.get_hilbert_space_dims()) { + throw std::runtime_error("As the dimensions of both the old and the new " + "state do no match, the " + "operator cannot act on the states."); + } + + // Prepare the operator for action + HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( + m_handle, m_liouvillian, state.get_impl(), next_state.get_impl(), + CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); + + // Query required workspace buffer size + std::size_t requiredBufferSize = 0; + HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( + m_handle, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); + + void *workspaceBuffer = nullptr; + if (requiredBufferSize > 0) { + // Allocate GPU storage for workspace buffer + const std::size_t bufferVolume = + requiredBufferSize / sizeof(std::complex); + workspaceBuffer = cudm_helper::create_array_gpu( + std::vector>(bufferVolume, {0.0, 0.0})); + + // Attach workspace buffer + HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( + m_handle, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); + } + + // Apply the operator action + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( + m_handle, m_liouvillian, t, 0, nullptr, state.get_impl(), + next_state.get_impl(), workspace, 0x0)); + HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); + + // Cleanup + cudm_helper::destroy_array_gpu(workspaceBuffer); + HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); + + return cudaq::state( + std::make_unique(std::move(next_state)).release()); + } + +private: + cudensitymatHandle_t m_handle; + cudensitymatOperator_t m_liouvillian; +}; +} // namespace + namespace cudaq { + +void runge_kutta::set_system(const SystemDynamics &system) { + m_system = system; +} + +void runge_kutta::set_state(cudaq::state initial_state, double t0) { + m_state = std::make_shared(initial_state); + m_t = t0; +} + +std::pair runge_kutta::get_state() { + auto *simState = cudaq::state_helper::getSimulationState(m_state.get()); + auto *castSimState = dynamic_cast(simState); + if (!castSimState) + throw std::runtime_error("Invalid state."); + + auto cudmState = + new CuDensityMatState(castSimState->get_handle(), *castSimState, + castSimState->get_hilbert_space_dims()); + + return std::make_pair(m_t, cudaq::state(cudmState)); +} + +void runge_kutta::integrate(double target_time) { + const auto asCudmState = [](cudaq::state &cudaqState) -> CuDensityMatState * { + auto *simState = cudaq::state_helper::getSimulationState(&cudaqState); + auto *castSimState = dynamic_cast(simState); + if (!castSimState) + throw std::runtime_error("Invalid state."); + return castSimState; + }; + auto &castSimState = *asCudmState(*m_state); + if (!m_stepper) { + static std::unordered_map> helpers; + if (helpers.find(castSimState.get_handle()) == helpers.end()) + helpers[castSimState.get_handle()] = + std::make_unique(castSimState.get_handle()); + auto &helper = *(helpers.find(castSimState.get_handle())->second); + auto liouvillian = helper.construct_liouvillian( + *m_system.hamiltonian, m_system.collapseOps, m_system.modeExtents, {}, + castSimState.is_density_matrix()); + m_stepper = + std::make_unique(castSimState.get_handle(), liouvillian); + } + const auto substeps = order.value_or(4); + while (m_t < target_time) { + double step_size = + std::min(dt.value_or(target_time - m_t), target_time - m_t); + + // std::cout << "Runge-Kutta step at time " << m_t + // << " with step size: " << step_size << std::endl; + + if (substeps == 1) { + // Euler method (1st order) + auto k1State = m_stepper->compute(*m_state, m_t, step_size, {}); + auto &k1 = *asCudmState(k1State); + // k1.dump(std::cout); + k1 *= step_size; + castSimState += k1; + } else if (substeps == 2) { + // Midpoint method (2nd order) + auto k1State = m_stepper->compute(*m_state, m_t, step_size, {}); + auto &k1 = *asCudmState(k1State); + k1 *= (step_size / 2.0); + + castSimState += k1; + + auto k2State = + m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size, {}); + auto &k2 = *asCudmState(k2State); + k2 *= (step_size / 2.0); + + castSimState += k2; + } else if (substeps == 4) { + // Runge-Kutta method (4th order) + auto k1State = m_stepper->compute(*m_state, m_t, step_size, {}); + auto &k1 = *asCudmState(k1State); + CuDensityMatState rho_temp = CuDensityMatState::clone(castSimState); + rho_temp += (k1 * (step_size / 2)); + + auto k2State = m_stepper->compute( + cudaq::state(new CuDensityMatState(std::move(rho_temp))), + m_t + step_size / 2.0, step_size, {}); + auto &k2 = *asCudmState(k2State); + CuDensityMatState rho_temp_2 = CuDensityMatState::clone(castSimState); + rho_temp_2 += (k2 * (step_size / 2)); + + auto k3State = m_stepper->compute( + cudaq::state(new CuDensityMatState(std::move(rho_temp_2))), + m_t + step_size / 2.0, step_size, {}); + auto &k3 = *asCudmState(k3State); + CuDensityMatState rho_temp_3 = CuDensityMatState::clone(castSimState); + rho_temp_3 += (k3 * step_size); + + auto k4State = m_stepper->compute( + cudaq::state(new CuDensityMatState(std::move(rho_temp_3))), + m_t + step_size, step_size, {}); + auto &k4 = *asCudmState(k4State); + castSimState += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (step_size / 6.0); + } else { + throw std::runtime_error("Invalid integrator order"); + } + + // Update time + m_t += step_size; + } +} + void runge_kutta_integrator::set_state(cudaq::state initial_state, double t0) { // TODO } diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index 775f8df671..ebf2c4750a 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include "CuDensityMatState.h" #include #include #include diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 15f6fd46ce..5c759f509d 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include "CuDensityMatState.h" #include // Initialize operator_sum diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 89c3da00d2..8178b229ea 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include "CuDensityMatState.h" #include #include #include @@ -55,7 +55,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { EXPECT_TRUE(state.is_initialized()); EXPECT_FALSE(state.is_density_matrix()); - EXPECT_NO_THROW(state.dump()); + EXPECT_NO_THROW(state.dump(std::cout)); } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { @@ -63,7 +63,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { EXPECT_TRUE(state.is_initialized()); EXPECT_TRUE(state.is_density_matrix()); - EXPECT_NO_THROW(state.dump()); + EXPECT_NO_THROW(state.dump(std::cout)); } TEST_F(CuDensityMatStateTest, InvalidInitialization) { @@ -81,7 +81,7 @@ TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); - EXPECT_NO_THROW(densityMatrixState.dump()); + EXPECT_NO_THROW(densityMatrixState.dump(std::cout)); } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { @@ -113,7 +113,7 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { cudm_state densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); - EXPECT_NO_THROW(densityMatrixState.dump()); + EXPECT_NO_THROW(densityMatrixState.dump(std::cout)); } TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { @@ -131,5 +131,5 @@ TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { cudm_state state(handle, stateVectorData, hilbertSpaceDims); - EXPECT_NO_THROW(state.dump()); + EXPECT_NO_THROW(state.dump(std::cout)); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 5eb583c22b..5bc4c0ca5b 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -9,7 +9,7 @@ #include "test_mocks.h" #include #include -#include +#include "CuDensityMatState.h" #include #include #include @@ -139,7 +139,7 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { auto output_state = time_stepper->compute(*input_state, 0.0, 1.0); std::cout << "Printing output_state ..." << std::endl; - output_state.dumpDeviceData(); + output_state.dump(std::cout); std::vector> output_state_vec(100); HANDLE_CUDA_ERROR( diff --git a/unittests/dynamics/test_evolve_api.cpp b/unittests/dynamics/test_evolve_api.cpp index 0fc4fbae41..f008024afb 100644 --- a/unittests/dynamics/test_evolve_api.cpp +++ b/unittests/dynamics/test_evolve_api.cpp @@ -28,22 +28,21 @@ TEST(EvolveAPITester, checkSimple) { cudaq::state::from_data(std::vector>{1.0, 0.0}); auto integrator = std::make_shared(); - integrator->dt = 0.001; integrator->order = 1; + integrator->dt = 0.001; auto result = cudaq::evolve(ham, dims, schedule, initialState, integrator, {}, {pauliZ}, true); - // TODO: enable runge_kutta (fixing the dependency to cudm types) - // EXPECT_TRUE(result.get_expectation_values().has_value()); - // EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); - // std::vector theoryResults; - // for (const auto &t : schedule) { - // const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); - // theoryResults.emplace_back(expected); - // } + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + for (const auto &t : schedule) { + const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); + theoryResults.emplace_back(expected); + } - // int count = 0; - // for (auto expVals : result.get_expectation_values().value()) { - // EXPECT_EQ(expVals.size(), 1); - // EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); - // } + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } } diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 62548c0933..93ccf1c480 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -6,10 +6,10 @@ // * the terms of the Apache License 2.0 which accompanies this distribution. * // ******************************************************************************/ +#include "CuDensityMatState.h" #include "common/EigenDense.h" +#include "cudaq/dynamics_integrators.h" #include "cudaq/evolution.h" -#include "cudaq/runge_kutta_integrator.h" -#include "cudm_state.h" #include #include #include @@ -30,7 +30,7 @@ TEST(EvolveTester, checkSimple) { auto initialState = cudaq::state::from_data(std::vector>{1.0, 0.0}); - cudaq::runge_kutta_integrator integrator; + cudaq::runge_kutta integrator; integrator.dt = 0.001; integrator.order = 1; auto result = cudaq::evolve_single(ham, dims, schedule, initialState, @@ -50,7 +50,7 @@ TEST(EvolveTester, checkSimple) { } } -TEST(EvolveTester, checkSimpleDensityMatrix) { +TEST(EvolveTester, checkDensityMatrixSimple) { const std::map dims = {{0, 2}}; cudaq::product_operator ham1 = (2.0 * M_PI * 0.1 * cudaq::spin_operator::x(0)); @@ -65,7 +65,7 @@ TEST(EvolveTester, checkSimpleDensityMatrix) { auto initialState = cudaq::state::from_data( std::vector>{1.0, 0.0, 0.0, 0.0}); - cudaq::runge_kutta_integrator integrator; + cudaq::runge_kutta integrator; integrator.dt = 0.001; integrator.order = 1; auto result = cudaq::evolve_single(ham, dims, schedule, initialState, @@ -117,7 +117,7 @@ TEST(EvolveTester, checkCompositeSystem) { cudaq::Schedule schedule(cudaq::linspace(0.0, 1, num_steps)); auto initialState = cudaq::state::from_data( std::make_pair(initial_state_vec.data(), initial_state_vec.size())); - cudaq::runge_kutta_integrator integrator; + cudaq::runge_kutta integrator; integrator.dt = 0.001; integrator.order = 4; @@ -172,7 +172,7 @@ TEST(EvolveTester, checkCompositeSystemWithCollapse) { cudaq::Schedule schedule(timeSteps); auto initialState = cudaq::state::from_data(std::make_pair(rho0.data(), rho0.size())); - cudaq::runge_kutta_integrator integrator; + cudaq::runge_kutta integrator; integrator.dt = 0.001; integrator.order = 4; constexpr double decayRate = 0.1; diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index c32ce8f978..b26e235264 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -6,8 +6,9 @@ // * the terms of the Apache License 2.0 which accompanies this distribution. * // ******************************************************************************/ +#include "CuDensityMatState.h" #include "cudaq/runge_kutta_integrator.h" -#include "cudm_state.h" +#include "cudm_helpers.h" #include "cudm_time_stepper.h" #include "test_mocks.h" #include From 69118b56b9076a6d04eb03c7b7964ce0ca23b2ce Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 17 Feb 2025 03:15:41 +0000 Subject: [PATCH 278/311] More cleanup: only use cudaq::state at the API surface Signed-off-by: Thien Nguyen --- runtime/cudaq/base_time_stepper.h | 13 - runtime/cudaq/runge_kutta_integrator.h | 41 -- runtime/nvqir/cudensitymat/cudm_state.cpp | 522 ------------------ runtime/nvqir/cudensitymat/cudm_state.h | 141 ----- .../nvqir/cudensitymat/cudm_time_stepper.cpp | 52 +- .../nvqir/cudensitymat/cudm_time_stepper.h | 16 +- .../cudensitymat/runge_kutta_integrator.cpp | 203 ++++--- unittests/dynamics/test_cudm_time_stepper.cpp | 81 +-- unittests/dynamics/test_evolve_single.cpp | 35 ++ .../dynamics/test_runge_kutta_integrator.cpp | 66 +-- 10 files changed, 243 insertions(+), 927 deletions(-) delete mode 100644 runtime/cudaq/runge_kutta_integrator.h delete mode 100644 runtime/nvqir/cudensitymat/cudm_state.cpp delete mode 100644 runtime/nvqir/cudensitymat/cudm_state.h diff --git a/runtime/cudaq/base_time_stepper.h b/runtime/cudaq/base_time_stepper.h index 9a859b9ea1..2742b2018d 100644 --- a/runtime/cudaq/base_time_stepper.h +++ b/runtime/cudaq/base_time_stepper.h @@ -10,19 +10,6 @@ #include "cudaq/qis/state.h" namespace cudaq { -template -class BaseTimeStepper { -public: - virtual ~BaseTimeStepper() = default; - - /// @brief Compute the next time step for the given quantum state. - /// @param state The quantum state to evolve. - /// @param t Current time. - /// @param step_size Time step size. - /// @return The updated quantum state after stepping. - virtual TState compute(TState &state, double t, double step_size) = 0; -}; - class TimeStepper { public: virtual ~TimeStepper() = default; diff --git a/runtime/cudaq/runge_kutta_integrator.h b/runtime/cudaq/runge_kutta_integrator.h deleted file mode 100644 index 7549a76a75..0000000000 --- a/runtime/cudaq/runge_kutta_integrator.h +++ /dev/null @@ -1,41 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "cudaq/base_integrator.h" -#include - -namespace cudaq { -class CuDensityMatState; -class cudm_time_stepper; -class runge_kutta_integrator : public BaseIntegrator { - -public: - std::optional order; - std::optional dt; - -public: - runge_kutta_integrator() = default; - runge_kutta_integrator(CuDensityMatState &&initial_state, double t0, - std::shared_ptr stepper, - int substeps = 4); - - void integrate(double target_time) override; - void set_state(cudaq::state initial_state, double t0) override; - void set_state(CuDensityMatState &&initial_state); - void set_stepper(std::shared_ptr stepper); - std::pair get_state() override; - std::pair get_cudm_state(); - -private: - std::unique_ptr m_state; - double m_t; - std::shared_ptr m_stepper; -}; -} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_state.cpp b/runtime/nvqir/cudensitymat/cudm_state.cpp deleted file mode 100644 index 7787c3a07e..0000000000 --- a/runtime/nvqir/cudensitymat/cudm_state.cpp +++ /dev/null @@ -1,522 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "CuDensityMatState.h" -#include "cudm_error_handling.h" -#include -#include -#include -#include -#include -#include -#include -#include - -namespace cudaq { -cudm_state::cudm_state(cudensitymatHandle_t handle, - const cudaq::state &simState, - const std::vector &hilbertSpaceDims) - : handle_(handle), state_(nullptr), hilbertSpaceDims_(hilbertSpaceDims) { - if (!simState.is_on_gpu()) - throw std::runtime_error("Unexpected state. This must be a state created " - "by the dynamics target"); - const bool isDensityMat = simState.get_tensor().get_num_elements() == - calculate_density_matrix_size(hilbertSpaceDims); - gpuDataSize_ = simState.get_tensor().get_num_elements(); - - const size_t dataSize = gpuDataSize_ * sizeof(std::complex); - HANDLE_CUDA_ERROR(cudaMalloc(reinterpret_cast(&gpuData_), dataSize)); - - HANDLE_CUDA_ERROR(cudaMemcpy(gpuData_, simState.get_tensor().data, dataSize, - cudaMemcpyDefault)); - - const cudensitymatStatePurity_t purity = isDensityMat - ? CUDENSITYMAT_STATE_PURITY_MIXED - : CUDENSITYMAT_STATE_PURITY_PURE; - HANDLE_CUDM_ERROR(cudensitymatCreateState( - handle_, purity, static_cast(hilbertSpaceDims.size()), - hilbertSpaceDims.data(), 1, CUDA_C_64F, &state_)); - - // Query the size of the quantum state storage - std::size_t storageSize{0}; // only one storage component (tensor) is needed - HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( - handle_, state_, - 1, // only one storage component - &storageSize)); // storage size in bytes - const std::size_t stateVolume = - storageSize / sizeof(std::complex); // quantum state tensor volume - // (number of elements) - assert(stateVolume == gpuDataSize_); - // std::cout << "Quantum state storage size (bytes) = " << storageSize - // << std::endl; - - // Attach initialized GPU storage to the input quantum state - HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( - handle_, state_, - 1, // only one storage component (tensor) - std::vector({gpuData_}) - .data(), // pointer to the GPU storage for the quantum state - std::vector({storageSize}) - .data())); // size of the GPU storage for the quantum state -} - -cudm_state cudm_state::zero_like(const cudm_state &other) { - cudm_state state; - state.handle_ = other.handle_; - state.hilbertSpaceDims_ = other.hilbertSpaceDims_; - state.gpuDataSize_ = other.gpuDataSize_; - const size_t dataSize = state.gpuDataSize_ * sizeof(std::complex); - HANDLE_CUDA_ERROR( - cudaMalloc(reinterpret_cast(&state.gpuData_), dataSize)); - HANDLE_CUDA_ERROR(cudaMemset(state.gpuData_, 0, dataSize)); - - const size_t expectedDensityMatrixSize = - calculate_density_matrix_size(state.hilbertSpaceDims_); - const bool isDensityMat = expectedDensityMatrixSize == state.gpuDataSize_; - const cudensitymatStatePurity_t purity = isDensityMat - ? CUDENSITYMAT_STATE_PURITY_MIXED - : CUDENSITYMAT_STATE_PURITY_PURE; - HANDLE_CUDM_ERROR(cudensitymatCreateState( - state.handle_, purity, - static_cast(state.hilbertSpaceDims_.size()), - state.hilbertSpaceDims_.data(), 1, CUDA_C_64F, &state.state_)); - - // Query the size of the quantum state storage - std::size_t storageSize{0}; // only one storage component (tensor) is needed - HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( - state.handle_, state.state_, - 1, // only one storage component - &storageSize)); // storage size in bytes - const std::size_t stateVolume = - storageSize / sizeof(std::complex); // quantum state tensor volume - // (number of elements) - assert(stateVolume == state.gpuDataSize_); - // std::cout << "Quantum state storage size (bytes) = " << storageSize - // << std::endl; - - // Attach initialized GPU storage to the input quantum state - HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( - state.handle_, state.state_, - 1, // only one storage component (tensor) - std::vector({state.gpuData_}) - .data(), // pointer to the GPU storage for the quantum state - std::vector({storageSize}) - .data())); // size of the GPU storage for the quantum state - return state; -} - -cudm_state cudm_state::clone(const cudm_state &other) { - cudm_state state; - state.handle_ = other.handle_; - state.hilbertSpaceDims_ = other.hilbertSpaceDims_; - state.gpuDataSize_ = other.gpuDataSize_; - const size_t dataSize = state.gpuDataSize_ * sizeof(std::complex); - HANDLE_CUDA_ERROR( - cudaMalloc(reinterpret_cast(&state.gpuData_), dataSize)); - HANDLE_CUDA_ERROR( - cudaMemcpy(state.gpuData_, other.gpuData_, dataSize, cudaMemcpyDefault)); - - const size_t expectedDensityMatrixSize = - calculate_density_matrix_size(state.hilbertSpaceDims_); - const bool isDensityMat = expectedDensityMatrixSize == state.gpuDataSize_; - const cudensitymatStatePurity_t purity = isDensityMat - ? CUDENSITYMAT_STATE_PURITY_MIXED - : CUDENSITYMAT_STATE_PURITY_PURE; - HANDLE_CUDM_ERROR(cudensitymatCreateState( - state.handle_, purity, - static_cast(state.hilbertSpaceDims_.size()), - state.hilbertSpaceDims_.data(), 1, CUDA_C_64F, &state.state_)); - - // Query the size of the quantum state storage - std::size_t storageSize{0}; // only one storage component (tensor) is needed - HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( - state.handle_, state.state_, - 1, // only one storage component - &storageSize)); // storage size in bytes - const std::size_t stateVolume = - storageSize / sizeof(std::complex); // quantum state tensor volume - // (number of elements) - assert(stateVolume == state.gpuDataSize_); - // std::cout << "Quantum state storage size (bytes) = " << storageSize - // << std::endl; - - // Attach initialized GPU storage to the input quantum state - HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( - state.handle_, state.state_, - 1, // only one storage component (tensor) - std::vector({state.gpuData_}) - .data(), // pointer to the GPU storage for the quantum state - std::vector({storageSize}) - .data())); // size of the GPU storage for the quantum state - return state; -} - -cudm_state::cudm_state(cudensitymatHandle_t handle, - const std::vector> &rawData, - const std::vector &hilbertSpaceDims) - : rawData_(rawData), gpuDataSize_(rawData.size()), state_(nullptr), - handle_(handle), hilbertSpaceDims_(hilbertSpaceDims) { - - if (rawData_.empty()) { - throw std::invalid_argument("Raw data cannot be empty."); - } - - // Allocate device memory - size_t dataSize = rawData_.size() * sizeof(std::complex); - HANDLE_CUDA_ERROR(cudaMalloc(reinterpret_cast(&gpuData_), dataSize)); - - // Copy data from host to device - HANDLE_CUDA_ERROR( - cudaMemcpy(gpuData_, rawData_.data(), dataSize, cudaMemcpyHostToDevice)); - - // Determine if this is a denisty matrix or state vector - size_t rawDataSize = rawData_.size(); - size_t expectedDensityMatrixSize = - calculate_density_matrix_size(hilbertSpaceDims); - size_t expectedStateVectorSize = - calculate_state_vector_size(hilbertSpaceDims); - - if (rawDataSize != expectedDensityMatrixSize && - rawDataSize != expectedStateVectorSize) { - throw std::invalid_argument( - "Invalid rawData size for the given Hilbert space dimensions."); - } - - cudensitymatStatePurity_t purity; - - if (rawDataSize == expectedDensityMatrixSize) { - purity = CUDENSITYMAT_STATE_PURITY_MIXED; - } else if (rawDataSize == expectedStateVectorSize) { - purity = CUDENSITYMAT_STATE_PURITY_PURE; - } - - HANDLE_CUDM_ERROR(cudensitymatCreateState( - handle_, purity, static_cast(hilbertSpaceDims.size()), - hilbertSpaceDims.data(), 1, CUDA_C_64F, &state_)); - - attach_storage(); -} - -cudm_state::cudm_state(cudm_state &&other) noexcept - : rawData_(std::move(other.rawData_)), gpuDataSize_(other.gpuDataSize_), - gpuData_(other.gpuData_), state_(other.state_), handle_(other.handle_), - hilbertSpaceDims_(std::move(other.hilbertSpaceDims_)) { - other.gpuData_ = nullptr; - other.state_ = nullptr; - other.gpuDataSize_ = 0; -} - -cudm_state &cudm_state::operator=(cudm_state &&other) noexcept { - if (this != &other) { - // Free existing resources - if (state_) { - cudensitymatDestroyState(state_); - } - if (gpuData_) { - cudaFree(gpuData_); - } - - // Move data from other - rawData_ = std::move(other.rawData_); - gpuData_ = other.gpuData_; - gpuDataSize_ = other.gpuDataSize_; - state_ = other.state_; - handle_ = other.handle_; - hilbertSpaceDims_ = std::move(other.hilbertSpaceDims_); - - // Nullify other - other.gpuData_ = nullptr; - other.state_ = nullptr; - other.gpuDataSize_ = 0; - } - - return *this; -} - -cudm_state::~cudm_state() { - if (state_) { - cudensitymatDestroyState(state_); - state_ = nullptr; - } - if (gpuData_) { - cudaFree(gpuData_); - gpuData_ = nullptr; - } -} - -bool cudm_state::is_initialized() const { return state_ != nullptr; } - -bool cudm_state::is_density_matrix() const { - if (!is_initialized()) { - return false; - } - - return gpuDataSize_ == calculate_density_matrix_size(hilbertSpaceDims_); -} - -std::vector> cudm_state::get_raw_data() const { - return rawData_; -} - -void *cudm_state::get_device_pointer() const { return gpuData_; } - -std::vector cudm_state::get_hilbert_space_dims() const { - return hilbertSpaceDims_; -} - -cudensitymatHandle_t cudm_state::get_handle() const { return handle_; } - -cudm_state cudm_state::operator+(const cudm_state &other) const { - if (gpuDataSize_ != other.gpuDataSize_) { - throw std::invalid_argument("State size mismatch for addition."); - } - - cudm_state result = cudm_state::clone(*this); - - double scalingFactor = 1.0; - double *gpuScalingFactor; - cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); - cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), - cudaMemcpyHostToDevice); - - HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( - handle_, other.get_impl(), result.get_impl(), gpuScalingFactor, 0)); - - cudaFree(gpuScalingFactor); - - return result; -} - -cudm_state &cudm_state::operator+=(const cudm_state &other) { - if (gpuDataSize_ != other.gpuDataSize_) { - throw std::invalid_argument( - fmt::format("State size mismatch for addition ({} vs {}).", - gpuDataSize_, other.gpuDataSize_)); - } - - double scalingFactor = 1.0; - double *gpuScalingFactor; - cudaMalloc(reinterpret_cast(&gpuScalingFactor), sizeof(double)); - cudaMemcpy(gpuScalingFactor, &scalingFactor, sizeof(double), - cudaMemcpyHostToDevice); - - HANDLE_CUDM_ERROR(cudensitymatStateComputeAccumulation( - handle_, other.get_impl(), state_, gpuScalingFactor, 0)); - - cudaFree(gpuScalingFactor); - - return *this; -} -cudm_state &cudm_state::operator*=(const std::complex &scalar) { - void *gpuScalar; - HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); - HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &scalar, sizeof(std::complex), - cudaMemcpyHostToDevice)); - - HANDLE_CUDM_ERROR( - cudensitymatStateComputeScaling(handle_, state_, gpuScalar, 0)); - - HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); - - return *this; -} - -cudm_state cudm_state::operator*(double scalar) const { - void *gpuScalar; - HANDLE_CUDA_ERROR(cudaMalloc(&gpuScalar, sizeof(std::complex))); - - std::complex complexScalar(scalar, 0.0); - HANDLE_CUDA_ERROR(cudaMemcpy(gpuScalar, &complexScalar, - sizeof(std::complex), - cudaMemcpyHostToDevice)); - - cudm_state result = cudm_state::clone(*this); - - HANDLE_CUDM_ERROR( - cudensitymatStateComputeScaling(handle_, result.state_, gpuScalar, 0)); - - HANDLE_CUDA_ERROR(cudaFree(gpuScalar)); - - return result; -} - -std::string cudm_state::dump() const { - if (!is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - - std::ostringstream oss; - oss << "State data: ["; - for (size_t i = 0; i < rawData_.size(); i++) { - oss << rawData_[i]; - if (i < rawData_.size() - 1) { - oss << ", "; - } - } - oss << "]"; - return oss.str(); -} - -void cudm_state::dumpDeviceData() const { - if (!is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - - std::vector> hostBuffer(gpuDataSize_); - HANDLE_CUDA_ERROR(cudaMemcpy(hostBuffer.data(), get_device_pointer(), - hostBuffer.size() * sizeof(std::complex), - cudaMemcpyDefault)); - std::cout << "State data: ["; - for (size_t i = 0; i < hostBuffer.size(); i++) { - std::cout << hostBuffer[i]; - if (i < hostBuffer.size() - 1) { - std::cout << ", "; - } - } - std::cout << "]\n"; -} - -cudm_state cudm_state::to_density_matrix() const { - if (!is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - - if (is_density_matrix()) { - throw std::runtime_error("State is already a density matrix."); - } - - std::vector> densityMatrix; - size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims_); - size_t expectedDensityMatrixSize = vectorSize * vectorSize; - densityMatrix.resize(expectedDensityMatrixSize); - - for (size_t i = 0; i < vectorSize; i++) { - for (size_t j = 0; j < vectorSize; j++) { - densityMatrix[i * vectorSize + j] = rawData_[i] * std::conj(rawData_[j]); - } - } - - return cudm_state(handle_, densityMatrix, hilbertSpaceDims_); -} - -cudensitymatState_t cudm_state::get_impl() const { - if (!is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - return state_; -} - -void cudm_state::attach_storage() { - if (!state_) { - throw std::runtime_error("State is not initialized."); - } - - if (rawData_.empty() || !gpuData_) { - throw std::runtime_error("Raw data is empty or device memory not " - "allocated. Cannot attach storage."); - } - - // Retrieve the number of state components - int32_t numStateComponents; - HANDLE_CUDM_ERROR( - cudensitymatStateGetNumComponents(handle_, state_, &numStateComponents)); - - // Retrieve the storage size for each component - std::vector componentBufferSizes(numStateComponents); - HANDLE_CUDM_ERROR(cudensitymatStateGetComponentStorageSize( - handle_, state_, numStateComponents, componentBufferSizes.data())); - - // Validate device memory - size_t totalSize = std::accumulate(componentBufferSizes.begin(), - componentBufferSizes.end(), 0); - if (totalSize > rawData_.size() * sizeof(std::complex)) { - throw std::invalid_argument( - "Device memory size is insufficient to cover all components."); - } - - // Attach storage for using device memory (gpuData_) - std::vector componentBuffers(numStateComponents); - size_t offset = 0; - for (int32_t i = 0; i < numStateComponents; i++) { - componentBuffers[i] = static_cast(gpuData_ + offset); - offset += componentBufferSizes[i] / sizeof(std::complex); - } - - HANDLE_CUDM_ERROR(cudensitymatStateAttachComponentStorage( - handle_, state_, numStateComponents, componentBuffers.data(), - componentBufferSizes.data())); -} - -size_t cudm_state::calculate_state_vector_size( - const std::vector &hilbertSpaceDims) { - return std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), 1, - std::multiplies<>()); -} - -size_t cudm_state::calculate_density_matrix_size( - const std::vector &hilbertSpaceDims) { - size_t vectorSize = calculate_state_vector_size(hilbertSpaceDims); - return vectorSize * vectorSize; -} - -// Initialize state based on InitialStateArgT -cudm_state cudm_state::create_initial_state( - cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, - const std::vector &hilbertSpaceDims, bool hasCollapseOps) { - size_t stateVectorSize = - std::accumulate(hilbertSpaceDims.begin(), hilbertSpaceDims.end(), - static_cast(1), std::multiplies<>{}); - - std::vector> rawData; - - if (std::holds_alternative(initialStateArg)) { - InitialState initialState = std::get(initialStateArg); - - if (initialState == InitialState::ZERO) { - rawData.resize(stateVectorSize, {0.0, 0.0}); - // |0> state - rawData[0] = {1.0, 0.0}; - } else if (initialState == InitialState::UNIFORM) { - rawData.resize(stateVectorSize, {1.0 / std::sqrt(stateVectorSize), 0.0}); - } else { - throw std::invalid_argument("Unsupported InitialState type."); - } - } else if (std::holds_alternative(initialStateArg)) { - void *runtimeState = std::get(initialStateArg); - if (!runtimeState) { - throw std::invalid_argument("Runtime state pointer is null."); - } - - try { - auto *externalData = - reinterpret_cast> *>(runtimeState); - - if (!externalData || externalData->empty()) { - throw std::invalid_argument( - "Runtime state contains invalid or empty data."); - } - - rawData = *externalData; - } catch (const std::exception &e) { - throw std::runtime_error("Failed to interpret runtime state: " + - std::string(e.what())); - } - } else { - throw std::invalid_argument("Unsupported InitialStateArgT type."); - } - - cudm_state state(handle, rawData, hilbertSpaceDims); - - // Convert to a density matrix if collapse operators are present. - if (hasCollapseOps && !state.is_density_matrix()) { - state = state.to_density_matrix(); - } - - return state; -} -} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_state.h b/runtime/nvqir/cudensitymat/cudm_state.h deleted file mode 100644 index 803064df24..0000000000 --- a/runtime/nvqir/cudensitymat/cudm_state.h +++ /dev/null @@ -1,141 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "cudm_error_handling.h" -#include "cudm_helpers.h" -#include -#include -#include -#include -#include - -namespace cudaq { -// Enum to specify the initial quantum state. -enum class InitialState { ZERO, UNIFORM }; - -using InitialStateArgT = std::variant; - -class cudm_state { -public: - /// @brief To initialize state with raw data. - explicit cudm_state(cudensitymatHandle_t handle, - const std::vector> &rawData, - const std::vector &hilbertSpaceDims); - - /// @brief To initialize state from a `cudaq::state` - explicit cudm_state(cudensitymatHandle_t handle, const cudaq::state &simState, - const std::vector &hilbertSpaceDims); - - // @brief Create a zero state - static cudm_state zero_like(const cudm_state &other); - static cudm_state clone(const cudm_state &other); - // Prevent copies (avoids double free issues) - cudm_state(const cudm_state &) = delete; - cudm_state &operator=(const cudm_state &) = delete; - - // Allow move semantics - cudm_state(cudm_state &&other) noexcept; - cudm_state &operator=(cudm_state &&other) noexcept; - - /// @brief Destructor to clean up resources - ~cudm_state(); - - /// @brief Factory method to create an initial state. - /// @param InitialStateArgT The type or representation of the initial state. - /// @param Dimensions of the Hilbert space. - /// @param hasCollapseOps Whether collapse operators are present. - /// @return A new 'cudm_state' initialized to the specified state. - static cudm_state create_initial_state( - cudensitymatHandle_t handle, const InitialStateArgT &initialStateArg, - const std::vector &hilbertSpaceDims, bool hasCollapseOps); - - /// @brief Check if the state is initialized. - /// @return True if the state is initialized, false otherwise. - bool is_initialized() const; - - /// @brief Check if the state is a density matrix. - /// @return True if the state is a density matrix, false otherwise. - bool is_density_matrix() const; - - /// @brief Dump the state data to a string for debugging purposes. - /// @return String representation of the state data. - std::string dump() const; - - /// @brief Dump the state data to the console for debugging purposes. - void dumpDeviceData() const; - - /// @brief Convert the state vector to a density matrix. - /// @return A new cudm_state representing the density matrix. - cudm_state to_density_matrix() const; - - /// @brief Get the underlying implementation (if any). - /// @return The underlying state implementation. - cudensitymatState_t get_impl() const; - - /// @brief Get a copy of the raw data representing the quantum state. - /// @return A copy of the raw data as a vector of complex numbers. - std::vector> get_raw_data() const; - - /// @brief Get the pointer to device memory buffer storing the state. - /// @return GPU device pointer - void *get_device_pointer() const; - - /// @brief Get a copy of the hilbert space dimensions for the quantum state. - /// @return A copy of the hilbert space dimensions of a vector of integers. - std::vector get_hilbert_space_dims() const; - - /// @brief Returns the handle - /// @return The handle associated with the state - cudensitymatHandle_t get_handle() const; - - /// @brief Addition operator (element-wise) - /// @return The new state after the summation of two states. - cudm_state operator+(const cudm_state &other) const; - - /// @brief Accumulation operator - /// @return Accumulates the summation of two states. - cudm_state &operator+=(const cudm_state &other); - - /// @brief Scalar multiplication operator - /// @return The new state after multiplying scalar with the current state. - cudm_state &operator*=(const std::complex &scalar); - - cudm_state operator*(double scalar) const; - -private: - // TODO: remove this host raw data, we shouldn't keep this as it will be - // decoupled to the GPU data. - std::vector> rawData_; - int64_t gpuDataSize_ = 0; - std::complex *gpuData_; - cudensitymatState_t state_; - cudensitymatHandle_t handle_; - std::vector hilbertSpaceDims_; - // Private default constructor - cudm_state() = default; - /// @brief Attach raw data storage to GPU - void attach_storage(); - - /// @brief Calculate the size of the state vector for the given Hilbert space - /// dimensions. - /// @param hilbertSpaceDims Hilbert space dimensions. - /// @return Size of the state vector. - static size_t - calculate_state_vector_size(const std::vector &hilbertSpaceDims); - - /// @brief Calculate the size of the density matrix for the given Hilbert - /// space dimensions. - /// @param hilbertSpaceDims Hilbert space dimensions. - /// @return Size of the density matrix. - static size_t - calculate_density_matrix_size(const std::vector &hilbertSpaceDims); -}; - -} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp index c8f3e2070a..61b7a1c35a 100644 --- a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp @@ -9,34 +9,28 @@ #include "cudm_time_stepper.h" #include "cudm_error_handling.h" #include "cudm_helpers.h" -#include namespace cudaq { -cudm_time_stepper::cudm_time_stepper(cudensitymatHandle_t handle, - cudensitymatOperator_t liouvillian) - : handle_(handle), liouvillian_(liouvillian) {} +cudmStepper::cudmStepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian) + : m_handle(handle), m_liouvillian(liouvillian){}; -cudm_state cudm_time_stepper::compute(cudm_state &state, double t, - double step_size) { +state cudmStepper::compute( + const state &inputState, double t, double step_size, + const std::unordered_map> ¶meters) { if (step_size == 0.0) { throw std::runtime_error("Step size cannot be zero."); } - if (!state.is_initialized()) { - throw std::runtime_error("State is not initialized."); - } - - if (!handle_) { - throw std::runtime_error("cudm_time_stepper handle is not initialized."); - } - - if (!liouvillian_) { - throw std::runtime_error("Liouvillian is not initialized."); - } - + auto *simState = + cudaq::state_helper::getSimulationState(const_cast(&inputState)); + auto *castSimState = dynamic_cast(simState); + if (!castSimState) + throw std::runtime_error("Invalid state."); + CuDensityMatState &state = *castSimState; // Prepare workspace cudensitymatWorkspaceDescriptor_t workspace; - HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(handle_, &workspace)); + HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(m_handle, &workspace)); // Query free gpu memory and allocate workspace buffer std::size_t freeMem = 0, totalMem = 0; @@ -45,27 +39,27 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, freeMem = static_cast(static_cast(freeMem) * 0.80); // Create a new state for the next step - cudm_state next_state = cudm_state::zero_like(state); + auto next_state = CuDensityMatState::zero_like(state); if (!next_state.is_initialized()) { throw std::runtime_error("Next state failed to initialize."); } if (state.get_hilbert_space_dims() != next_state.get_hilbert_space_dims()) { - throw std::runtime_error( - "As the dimensions of both the old and the new state do no match, the " - "operator cannot act on the states."); + throw std::runtime_error("As the dimensions of both the old and the new " + "state do no match, the " + "operator cannot act on the states."); } // Prepare the operator for action HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( - handle_, liouvillian_, state.get_impl(), next_state.get_impl(), + m_handle, m_liouvillian, state.get_impl(), next_state.get_impl(), CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); // Query required workspace buffer size std::size_t requiredBufferSize = 0; HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( - handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + m_handle, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); void *workspaceBuffer = nullptr; @@ -78,14 +72,14 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, // Attach workspace buffer HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( - handle_, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, + m_handle, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); } // Apply the operator action HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( - handle_, liouvillian_, t, 0, nullptr, state.get_impl(), + m_handle, m_liouvillian, t, 0, nullptr, state.get_impl(), next_state.get_impl(), workspace, 0x0)); HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); @@ -93,6 +87,8 @@ cudm_state cudm_time_stepper::compute(cudm_state &state, double t, cudm_helper::destroy_array_gpu(workspaceBuffer); HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); - return next_state; + return cudaq::state( + std::make_unique(std::move(next_state)).release()); } + } // namespace cudaq \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/cudm_time_stepper.h b/runtime/nvqir/cudensitymat/cudm_time_stepper.h index 5de78ccd76..7976579d0a 100644 --- a/runtime/nvqir/cudensitymat/cudm_time_stepper.h +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.h @@ -8,20 +8,22 @@ #pragma once -#include "cudaq/base_time_stepper.h" #include "CuDensityMatState.h" +#include "cudaq/base_time_stepper.h" #include namespace cudaq { -class cudm_time_stepper : public BaseTimeStepper { +class cudmStepper : public TimeStepper { public: - explicit cudm_time_stepper(cudensitymatHandle_t handle, - cudensitymatOperator_t liouvillian); + explicit cudmStepper(cudensitymatHandle_t handle, + cudensitymatOperator_t liouvillian); - cudm_state compute(cudm_state &state, double t, double step_size); + state compute(const state &inputState, double t, double step_size, + const std::unordered_map> + ¶meters) override; private: - cudensitymatHandle_t handle_; - cudensitymatOperator_t liouvillian_; + cudensitymatHandle_t m_handle; + cudensitymatOperator_t m_liouvillian; }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index c8eec0d4ee..2f1ac7c523 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -6,7 +6,6 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/runge_kutta_integrator.h" #include "CuDensityMatState.h" #include "cudaq/dynamics_integrators.h" #include "cudm_error_handling.h" @@ -211,105 +210,105 @@ void runge_kutta::integrate(double target_time) { } } -void runge_kutta_integrator::set_state(cudaq::state initial_state, double t0) { - // TODO -} -std::pair runge_kutta_integrator::get_state() { - // TODO: - return std::make_pair(0.0, cudaq::state(nullptr)); -} - -// FIXME: remove this -std::pair runge_kutta_integrator::get_cudm_state() { - return std::make_pair(m_t, m_state.get()); -} - -void runge_kutta_integrator::integrate(double target_time) { - if (!m_stepper) { - throw std::runtime_error("Time stepper is not initialized."); - } - - if (dt.has_value() && dt.value() <= 0.0) { - throw std::invalid_argument("Invalid time step size for integration."); - } - - if (!m_state) { - throw std::runtime_error("Initial state has not been set."); - } - const auto substeps = order.value_or(4); - while (m_t < target_time) { - double step_size = - std::min(dt.value_or(target_time - m_t), target_time - m_t); - - // std::cout << "Runge-Kutta step at time " << m_t - // << " with step size: " << step_size << std::endl; - - if (substeps == 1) { - // Euler method (1st order) - cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); - k1 *= step_size; - *m_state += k1; - } else if (substeps == 2) { - // Midpoint method (2nd order) - cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); - k1 *= (step_size / 2.0); - - *m_state += k1; - - cudm_state k2 = - m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); - k2 *= (step_size / 2.0); - - *m_state += k2; - } else if (substeps == 4) { - // Runge-Kutta method (4th order) - cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); - - cudm_state rho_temp = cudm_state::clone(*m_state); - rho_temp += (k1 * (step_size / 2)); - - cudm_state k2 = - m_stepper->compute(rho_temp, m_t + step_size / 2.0, step_size); - - cudm_state rho_temp_2 = cudm_state::clone(*m_state); - rho_temp_2 += (k2 * (step_size / 2)); - - cudm_state k3 = - m_stepper->compute(rho_temp_2, m_t + step_size / 2.0, step_size); - - cudm_state rho_temp_3 = cudm_state::clone(*m_state); - rho_temp_3 += (k3 * step_size); - - cudm_state k4 = - m_stepper->compute(rho_temp_3, m_t + step_size, step_size); - - *m_state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (step_size / 6.0); - } else { - throw std::runtime_error("Invalid integrator order"); - } - - // Update time - m_t += step_size; - } - - // std::cout << "Integration complete. Final time: " << m_t << std::endl; -} - -// TODO: remove this -runge_kutta_integrator::runge_kutta_integrator( - cudm_state &&initial_state, double t0, - std::shared_ptr stepper, int substeps) - : m_t(t0), m_stepper(stepper), order(substeps) { - - m_state = std::make_unique(std::move(initial_state)); -} -void runge_kutta_integrator::set_stepper( - std::shared_ptr stepper) { - m_stepper = stepper; -} - -void runge_kutta_integrator::set_state(cudm_state &&initial_state) { - m_state = std::make_unique(std::move(initial_state)); - m_t = 0.0; -} +// void runge_kutta_integrator::set_state(cudaq::state initial_state, double t0) { +// // TODO +// } +// std::pair runge_kutta_integrator::get_state() { +// // TODO: +// return std::make_pair(0.0, cudaq::state(nullptr)); +// } + +// // FIXME: remove this +// std::pair runge_kutta_integrator::get_cudm_state() { +// return std::make_pair(m_t, m_state.get()); +// } + +// void runge_kutta_integrator::integrate(double target_time) { +// if (!m_stepper) { +// throw std::runtime_error("Time stepper is not initialized."); +// } + +// if (dt.has_value() && dt.value() <= 0.0) { +// throw std::invalid_argument("Invalid time step size for integration."); +// } + +// if (!m_state) { +// throw std::runtime_error("Initial state has not been set."); +// } +// const auto substeps = order.value_or(4); +// while (m_t < target_time) { +// double step_size = +// std::min(dt.value_or(target_time - m_t), target_time - m_t); + +// // std::cout << "Runge-Kutta step at time " << m_t +// // << " with step size: " << step_size << std::endl; + +// if (substeps == 1) { +// // Euler method (1st order) +// cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); +// k1 *= step_size; +// *m_state += k1; +// } else if (substeps == 2) { +// // Midpoint method (2nd order) +// cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); +// k1 *= (step_size / 2.0); + +// *m_state += k1; + +// cudm_state k2 = +// m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); +// k2 *= (step_size / 2.0); + +// *m_state += k2; +// } else if (substeps == 4) { +// // Runge-Kutta method (4th order) +// cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); + +// cudm_state rho_temp = cudm_state::clone(*m_state); +// rho_temp += (k1 * (step_size / 2)); + +// cudm_state k2 = +// m_stepper->compute(rho_temp, m_t + step_size / 2.0, step_size); + +// cudm_state rho_temp_2 = cudm_state::clone(*m_state); +// rho_temp_2 += (k2 * (step_size / 2)); + +// cudm_state k3 = +// m_stepper->compute(rho_temp_2, m_t + step_size / 2.0, step_size); + +// cudm_state rho_temp_3 = cudm_state::clone(*m_state); +// rho_temp_3 += (k3 * step_size); + +// cudm_state k4 = +// m_stepper->compute(rho_temp_3, m_t + step_size, step_size); + +// *m_state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (step_size / 6.0); +// } else { +// throw std::runtime_error("Invalid integrator order"); +// } + +// // Update time +// m_t += step_size; +// } + +// // std::cout << "Integration complete. Final time: " << m_t << std::endl; +// } + +// // TODO: remove this +// runge_kutta_integrator::runge_kutta_integrator( +// cudm_state &&initial_state, double t0, +// std::shared_ptr stepper, int substeps) +// : m_t(t0), m_stepper(stepper), order(substeps) { + +// m_state = std::make_unique(std::move(initial_state)); +// } +// void runge_kutta_integrator::set_stepper( +// std::shared_ptr stepper) { +// m_stepper = stepper; +// } + +// void runge_kutta_integrator::set_state(cudm_state &&initial_state) { +// m_state = std::make_unique(std::move(initial_state)); +// m_t = 0.0; +// } } // namespace cudaq diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 5bc4c0ca5b..af3cfc8abf 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -6,11 +6,11 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatState.h" +#include "cudm_time_stepper.h" #include "test_mocks.h" #include #include -#include "CuDensityMatState.h" -#include #include #include #include @@ -21,8 +21,8 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { protected: cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; - std::unique_ptr time_stepper_; - std::unique_ptr state_; + std::unique_ptr time_stepper_; + cudaq::state state_ = cudaq::state(nullptr); std::unique_ptr helper_; void SetUp() override { @@ -36,12 +36,14 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { liouvillian_ = mock_liouvillian(handle_); // Initialize the time stepper - time_stepper_ = std::make_unique(handle_, liouvillian_); - - state_ = std::make_unique(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()); - - ASSERT_TRUE(state_->is_initialized()); + time_stepper_ = std::make_unique(handle_, liouvillian_); + + state_ = cudaq::state::from_data(mock_initial_state_data()); + auto *simState = cudaq::state_helper::getSimulationState(&state_); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, mock_hilbert_space_dims()); + ASSERT_TRUE(castSimState->is_initialized()); } void TearDown() override { @@ -51,30 +53,33 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { } }; -// Test initialization of cudm_time_stepper +// Test initialization of cudmStepper TEST_F(CuDensityMatTimeStepperTest, Initialization) { ASSERT_NE(time_stepper_, nullptr); - ASSERT_TRUE(state_->is_initialized()); - ASSERT_FALSE(state_->is_density_matrix()); + auto *simState = cudaq::state_helper::getSimulationState(&state_); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + ASSERT_TRUE(castSimState->is_initialized()); + ASSERT_FALSE(castSimState->is_density_matrix()); } // Test a single compute step TEST_F(CuDensityMatTimeStepperTest, ComputeStep) { - ASSERT_TRUE(state_->is_initialized()); - EXPECT_NO_THROW(time_stepper_->compute(*state_, 0.0, 1.0)); - ASSERT_TRUE(state_->is_initialized()); + EXPECT_NO_THROW(time_stepper_->compute(state_, 0.0, 1.0, {})); } // Compute step when handle is uninitialized TEST_F(CuDensityMatTimeStepperTest, ComputeStepUninitializedHandle) { - cudm_time_stepper invalidStepper(nullptr, liouvillian_); - EXPECT_THROW(invalidStepper.compute(*state_, 0.0, 1.0), std::runtime_error); + cudmStepper invalidStepper(nullptr, liouvillian_); + EXPECT_THROW(invalidStepper.compute(state_, 0.0, 1.0, {}), + std::runtime_error); } // Compute step when liouvillian is missing TEST_F(CuDensityMatTimeStepperTest, ComputeStepNoLiouvillian) { - cudm_time_stepper invalidStepper(handle_, nullptr); - EXPECT_THROW(invalidStepper.compute(*state_, 0.0, 1.0), std::runtime_error); + cudmStepper invalidStepper(handle_, nullptr); + EXPECT_THROW(invalidStepper.compute(state_, 0.0, 1.0, {}), + std::runtime_error); } // Compute step with mismatched dimensions @@ -88,30 +93,34 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepMistmatchedDimensions) { // Compute step with zero step size TEST_F(CuDensityMatTimeStepperTest, ComputeStepZeroStepSize) { - EXPECT_THROW(time_stepper_->compute(*state_, 0.0, 0.0), std::runtime_error); + EXPECT_THROW(time_stepper_->compute(state_, 0.0, 0.0, {}), + std::runtime_error); } // Compute step with large time values TEST_F(CuDensityMatTimeStepperTest, ComputeStepLargeTimeValues) { - EXPECT_NO_THROW(time_stepper_->compute(*state_, 1e6, 1e3)); + EXPECT_NO_THROW(time_stepper_->compute(state_, 1e6, 1e3, {})); } TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { const std::vector> initialState = { {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {4}; - auto inputState = std::make_unique(handle_, initialState, dims); + auto inputState = cudaq::state::from_data(initialState); + auto *simState = cudaq::state_helper::getSimulationState(&inputState); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); + auto op = cudaq::matrix_operator::create(0); auto cudmOp = helper_->convert_to_cudensitymat_operator( {}, op, dims); // Initialize the time stepper - auto time_stepper = std::make_unique(handle_, cudmOp); - auto outputState = time_stepper->compute(*inputState, 0.0, 1.0); + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(inputState, 0.0, 1.0, {}); std::vector> outputStateVec(4); - HANDLE_CUDA_ERROR(cudaMemcpy( - outputStateVec.data(), outputState.get_device_pointer(), - outputStateVec.size() * sizeof(std::complex), cudaMemcpyDefault)); + outputState.to_host(outputStateVec.data(), outputStateVec.size()); // Create operator move the state up 1 step. const std::vector> expectedOutputState = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; @@ -128,24 +137,24 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { initial_state[5 * 10 + 5] = {1.0, 0.0}; const std::vector dims = {10}; - auto input_state = std::make_unique(handle_, initial_state, dims); + auto input_state = cudaq::state::from_data(initial_state); + auto *simState = cudaq::state_helper::getSimulationState(&input_state); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); auto c_op_0 = cudaq::matrix_operator::annihilate(0); auto cudm_lindblad_op = helper_->compute_lindblad_operator({c_op_0.to_matrix({{0, 10}})}, dims); - auto time_stepper = - std::make_unique(handle_, cudm_lindblad_op); - auto output_state = time_stepper->compute(*input_state, 0.0, 1.0); + auto time_stepper = std::make_unique(handle_, cudm_lindblad_op); + auto output_state = time_stepper->compute(input_state, 0.0, 1.0, {}); std::cout << "Printing output_state ..." << std::endl; output_state.dump(std::cout); std::vector> output_state_vec(100); - HANDLE_CUDA_ERROR( - cudaMemcpy(output_state_vec.data(), output_state.get_device_pointer(), - output_state_vec.size() * sizeof(std::complex), - cudaMemcpyDefault)); + output_state.to_host(output_state_vec.data(), output_state_vec.size()); helper_->print_complex_vector(output_state_vec); diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 93ccf1c480..fae5ec48f1 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -50,6 +50,41 @@ TEST(EvolveTester, checkSimple) { } } +TEST(EvolveTester, checkSimpleRK4) { + const std::map dims = {{0, 2}}; + cudaq::product_operator ham1 = + (2.0 * M_PI * 0.1 * cudaq::spin_operator::x(0)); + cudaq::operator_sum ham(ham1); + + constexpr int numSteps = 10; + cudaq::Schedule schedule(cudaq::linspace(0.0, 1.0, numSteps)); + + cudaq::product_operator pauliZ_t = + cudaq::spin_operator::z(0); + cudaq::operator_sum pauliZ(pauliZ_t); + auto initialState = + cudaq::state::from_data(std::vector>{1.0, 0.0}); + + cudaq::runge_kutta integrator; + integrator.dt = 0.001; + integrator.order = 4; + auto result = cudaq::evolve_single(ham, dims, schedule, initialState, + integrator, {}, {&pauliZ}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + for (const auto &t : schedule) { + const double expected = std::cos(2 * 2.0 * M_PI * 0.1 * t); + theoryResults.emplace_back(expected); + } + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } +} + TEST(EvolveTester, checkDensityMatrixSimple) { const std::map dims = {{0, 2}}; cudaq::product_operator ham1 = diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index b26e235264..937875d36d 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -7,7 +7,7 @@ // ******************************************************************************/ #include "CuDensityMatState.h" -#include "cudaq/runge_kutta_integrator.h" +#include "cudaq/dynamics_integrators.h" #include "cudm_helpers.h" #include "cudm_time_stepper.h" #include "test_mocks.h" @@ -21,8 +21,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { protected: cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; - std::shared_ptr time_stepper_; - std::unique_ptr integrator_; + std::unique_ptr integrator_; std::unique_ptr state_; void SetUp() override { @@ -32,10 +31,6 @@ class RungeKuttaIntegratorTest : public ::testing::Test { // Create a mock Liouvillian liouvillian_ = mock_liouvillian(handle_); - // Initialize the time stepper - time_stepper_ = std::make_shared(handle_, liouvillian_); - ASSERT_NE(time_stepper_, nullptr); - // Create initial state state_ = std::make_unique(handle_, mock_initial_state_data(), mock_hilbert_space_dims()); @@ -44,9 +39,9 @@ class RungeKuttaIntegratorTest : public ::testing::Test { double t0 = 0.0; // Initialize the integrator (using substeps = 4, for Runge-Kutta method) - ASSERT_NO_THROW(integrator_ = std::make_unique( - std::move(*state_), t0, time_stepper_, 4)); + ASSERT_NO_THROW(integrator_ = std::make_unique()); ASSERT_NE(integrator_, nullptr); + integrator_->order = 4; } void TearDown() override { @@ -63,7 +58,7 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - // auto integrator = std::make_unique( + // auto integrator = std::make_unique( // cudm_state(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), 0.0, time_stepper_, 1); // integrator->set_option("dt", 0.1); @@ -72,7 +67,7 @@ TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - // auto midpointIntegrator = std::make_unique( + // auto midpointIntegrator = std::make_unique( // cudm_state(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); // midpointIntegrator->set_option("dt", 0.1); @@ -112,7 +107,7 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - // auto integrator_missing_dt = std::make_unique( + // auto integrator_missing_dt = std::make_unique( // cudm_state(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); @@ -144,7 +139,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - // EXPECT_THROW(std::make_unique( + // EXPECT_THROW(std::make_unique( // cudm_state(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), // 0.0, time_stepper_, 3), @@ -153,39 +148,38 @@ TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { cudm_helper helper(handle_); - const std::vector> initialState = {{1.0, 0.0}, + const std::vector> initialStateVec = {{1.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {2}; auto spin_op_x = cudaq::spin_operator::x(0); + cudaq::product_operator ham1 = 2.0 * M_PI * 0.1 * spin_op_x; + cudaq::operator_sum ham(ham1); + SystemDynamics system; + system.hamiltonian = &ham; + system.modeExtents = dims; for (int integratorOrder : {1, 2, 4}) { std::cout << "Test RK order " << integratorOrder << "\n"; - cudaq::product_operator ham1 = - (std::complex{0.0, -1.0} * 2.0 * M_PI * 0.1 * spin_op_x); - cudaq::operator_sum ham(ham1); - auto cudmOp = - helper.convert_to_cudensitymat_operator({}, ham, - dims); - auto time_stepper = std::make_shared(handle_, cudmOp); - - auto integrator = std::make_unique( - cudm_state(handle_, initialState, dims), 0.0, time_stepper, - integratorOrder); - integrator->dt = 0.001; - + cudaq::runge_kutta integrator; + integrator.dt = 0.001; + integrator.order = integratorOrder; constexpr std::size_t numDataPoints = 10; double t = 0.0; + auto initialState = cudaq::state::from_data(initialStateVec); + // initialState.dump(); + auto *simState = cudaq::state_helper::getSimulationState(&initialState); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); + integrator.set_state(initialState, 0.0); + integrator.set_system(system); std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { - integrator->integrate(i); - auto [t, statePtr] = integrator->get_cudm_state(); - auto &state = *statePtr; + integrator.integrate(i); + auto [t, state] = integrator.get_state(); // std::cout << "Time = " << t << "\n"; - // state.dumpDeviceData(); - HANDLE_CUDA_ERROR( - cudaMemcpy(outputStateVec.data(), state.get_device_pointer(), - outputStateVec.size() * sizeof(std::complex), - cudaMemcpyDefault)); + // state.dump(); + state.to_host(outputStateVec.data(), outputStateVec.size()); // Check state vector norm EXPECT_NEAR(std::norm(outputStateVec[0]) + std::norm(outputStateVec[1]), 1.0, 1e-2); @@ -195,8 +189,6 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { EXPECT_NEAR(outputStateVec[0].real(), std::cos(2.0 * M_PI * 0.1 * t), 1e-2); } - - HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); } // Add test to test tensor_callback From 9fdf186ff5520771d6ec3eb4c1e0979f0144afc7 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 17 Feb 2025 03:20:58 +0000 Subject: [PATCH 279/311] More cleanup: remove the type alias Signed-off-by: Thien Nguyen --- .../nvqir/cudensitymat/CuDensityMatState.h | 2 - runtime/nvqir/cudensitymat/cudm_solver.cpp | 60 ------ runtime/nvqir/cudensitymat/cudm_solver.h | 58 ------ .../cudensitymat/runge_kutta_integrator.cpp | 192 ------------------ unittests/dynamics/test_cudm_expectation.cpp | 4 +- unittests/dynamics/test_cudm_helpers.cpp | 6 +- unittests/dynamics/test_cudm_state.cpp | 26 +-- unittests/dynamics/test_cudm_time_stepper.cpp | 4 +- .../dynamics/test_runge_kutta_integrator.cpp | 12 +- 9 files changed, 26 insertions(+), 338 deletions(-) delete mode 100644 runtime/nvqir/cudensitymat/cudm_solver.cpp delete mode 100644 runtime/nvqir/cudensitymat/cudm_solver.h diff --git a/runtime/nvqir/cudensitymat/CuDensityMatState.h b/runtime/nvqir/cudensitymat/CuDensityMatState.h index f11a20d54f..630663b4ce 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatState.h +++ b/runtime/nvqir/cudensitymat/CuDensityMatState.h @@ -148,6 +148,4 @@ class CuDensityMatState : public cudaq::SimulationState { CuDensityMatState operator*(double scalar) const; }; /// @endcond - -typedef CuDensityMatState cudm_state; } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_solver.cpp b/runtime/nvqir/cudensitymat/cudm_solver.cpp deleted file mode 100644 index dd401bac1f..0000000000 --- a/runtime/nvqir/cudensitymat/cudm_solver.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudm_solver.h" -#include "cudm_helpers.h" -#include "CuDensityMatState.h" -#include "cudm_time_stepper.h" - -namespace cudaq { -cudm_solver::cudm_solver(const Config &config) : config_(config) { - validate_config(); -} - -void cudm_solver::validate_config() { - if (config_.dimensions.empty()) { - throw std::invalid_argument("Dimensions map cannot be empty."); - } - - if (config_.hamiltonian.get_terms().empty()) { - throw std::invalid_argument("Hamiltonian must have at least one term."); - } - - if (config_.dimensions.empty()) { - throw std::invalid_argument("Schedule cannot be empty."); - } -} - -cudm_state cudm_solver::initialize_state() { - std::vector mode_extents; - for (const auto &[key, value] : config_.dimensions) { - mode_extents.push_back(value); - } - - return cudm_state::create_initial_state(config_.initial_state, mode_extents, - !config_.collapse_operators.empty()); -} - -cudensitymatOperator_t cudm_solver::construct_liouvillian( - cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, - const std::vector &collapse_operators, - bool me_solve) { - return construct_liovillian(handle, hamiltonian, collapse_operators, - me_solve ? 1.0 : 0.0); -} - -void cudm_solver::evolve( - cudm_state &state, cudensitymatOperator_t &liouvillian, - const std::vector &observable_ops, - evolve_result &result) { - auto handle = state.get_impl(); - - // Initialize the stepper - cudm_time_stepper time_stepper(handle, liouvillian); -} -} // namespace cudaq \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/cudm_solver.h b/runtime/nvqir/cudensitymat/cudm_solver.h deleted file mode 100644 index 17bc1fd9ae..0000000000 --- a/runtime/nvqir/cudensitymat/cudm_solver.h +++ /dev/null @@ -1,58 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "cudm_helpers.h> -#include "cudm_state.h> -#include "runtime/common/EvolveResult.h" -#include -#include -#include -#include -#include -#include -#include - -namespace cudaq { - -// Configuration struct for the solver -struct Config { - std::map dimensions; // Hilbert space dimensions - operator_sum hamiltonian; // Hamiltonian operator - std::vector collapse_operators; // Collapse operators - std::vector observables; // Observables to evaluate - std::variant>> - initial_state; // Initial state - Schedule schedule; // Evolution schedule - bool store_intermediate_results = false; // Flag to store intermediate states -}; - -class cudm_solver { -public: - cudm_solver(const Config &config); - - void validate_config(); - - cudm_state initialize_state(); - - void evolve(cudm_state &state, cudensitymatOperator_t &liouvillian, - const std::vector &obervable_ops, - evolve_result &result); - - evolve_result evolve_dynamics(); - - cudensitymatOperator_t construct_liouvillian( - cudensitymatHandle_t handle, const cudensitymatOperator_t &hamiltonian, - const std::vector &collapse_operators, - bool me_solve); - -private: - Config config_; -}; -} // namespace cudaq \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index 2f1ac7c523..4568d0c3b2 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -11,96 +11,6 @@ #include "cudm_error_handling.h" #include "cudm_helpers.h" #include "cudm_time_stepper.h" -namespace { -using namespace cudaq; -class cudmStepper : public TimeStepper { -public: - explicit cudmStepper(cudensitymatHandle_t handle, - cudensitymatOperator_t liouvillian) - : m_handle(handle), m_liouvillian(liouvillian){}; - - state compute(const state &inputState, double t, double step_size, - const std::unordered_map> - ¶meters) override { - if (step_size == 0.0) { - throw std::runtime_error("Step size cannot be zero."); - } - - auto *simState = cudaq::state_helper::getSimulationState( - const_cast(&inputState)); - auto *castSimState = dynamic_cast(simState); - if (!castSimState) - throw std::runtime_error("Invalid state."); - CuDensityMatState &state = *castSimState; - // Prepare workspace - cudensitymatWorkspaceDescriptor_t workspace; - HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(m_handle, &workspace)); - - // Query free gpu memory and allocate workspace buffer - std::size_t freeMem = 0, totalMem = 0; - HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); - // Take 80% of free memory - freeMem = static_cast(static_cast(freeMem) * 0.80); - - // Create a new state for the next step - auto next_state = CuDensityMatState::zero_like(state); - - if (!next_state.is_initialized()) { - throw std::runtime_error("Next state failed to initialize."); - } - - if (state.get_hilbert_space_dims() != next_state.get_hilbert_space_dims()) { - throw std::runtime_error("As the dimensions of both the old and the new " - "state do no match, the " - "operator cannot act on the states."); - } - - // Prepare the operator for action - HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( - m_handle, m_liouvillian, state.get_impl(), next_state.get_impl(), - CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); - - // Query required workspace buffer size - std::size_t requiredBufferSize = 0; - HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( - m_handle, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, - CUDENSITYMAT_WORKSPACE_SCRATCH, &requiredBufferSize)); - - void *workspaceBuffer = nullptr; - if (requiredBufferSize > 0) { - // Allocate GPU storage for workspace buffer - const std::size_t bufferVolume = - requiredBufferSize / sizeof(std::complex); - workspaceBuffer = cudm_helper::create_array_gpu( - std::vector>(bufferVolume, {0.0, 0.0})); - - // Attach workspace buffer - HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( - m_handle, workspace, CUDENSITYMAT_MEMSPACE_DEVICE, - CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); - } - - // Apply the operator action - HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); - HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( - m_handle, m_liouvillian, t, 0, nullptr, state.get_impl(), - next_state.get_impl(), workspace, 0x0)); - HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); - - // Cleanup - cudm_helper::destroy_array_gpu(workspaceBuffer); - HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); - - return cudaq::state( - std::make_unique(std::move(next_state)).release()); - } - -private: - cudensitymatHandle_t m_handle; - cudensitymatOperator_t m_liouvillian; -}; -} // namespace - namespace cudaq { void runge_kutta::set_system(const SystemDynamics &system) { @@ -209,106 +119,4 @@ void runge_kutta::integrate(double target_time) { m_t += step_size; } } - -// void runge_kutta_integrator::set_state(cudaq::state initial_state, double t0) { -// // TODO -// } -// std::pair runge_kutta_integrator::get_state() { -// // TODO: -// return std::make_pair(0.0, cudaq::state(nullptr)); -// } - -// // FIXME: remove this -// std::pair runge_kutta_integrator::get_cudm_state() { -// return std::make_pair(m_t, m_state.get()); -// } - -// void runge_kutta_integrator::integrate(double target_time) { -// if (!m_stepper) { -// throw std::runtime_error("Time stepper is not initialized."); -// } - -// if (dt.has_value() && dt.value() <= 0.0) { -// throw std::invalid_argument("Invalid time step size for integration."); -// } - -// if (!m_state) { -// throw std::runtime_error("Initial state has not been set."); -// } -// const auto substeps = order.value_or(4); -// while (m_t < target_time) { -// double step_size = -// std::min(dt.value_or(target_time - m_t), target_time - m_t); - -// // std::cout << "Runge-Kutta step at time " << m_t -// // << " with step size: " << step_size << std::endl; - -// if (substeps == 1) { -// // Euler method (1st order) -// cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); -// k1 *= step_size; -// *m_state += k1; -// } else if (substeps == 2) { -// // Midpoint method (2nd order) -// cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); -// k1 *= (step_size / 2.0); - -// *m_state += k1; - -// cudm_state k2 = -// m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size); -// k2 *= (step_size / 2.0); - -// *m_state += k2; -// } else if (substeps == 4) { -// // Runge-Kutta method (4th order) -// cudm_state k1 = m_stepper->compute(*m_state, m_t, step_size); - -// cudm_state rho_temp = cudm_state::clone(*m_state); -// rho_temp += (k1 * (step_size / 2)); - -// cudm_state k2 = -// m_stepper->compute(rho_temp, m_t + step_size / 2.0, step_size); - -// cudm_state rho_temp_2 = cudm_state::clone(*m_state); -// rho_temp_2 += (k2 * (step_size / 2)); - -// cudm_state k3 = -// m_stepper->compute(rho_temp_2, m_t + step_size / 2.0, step_size); - -// cudm_state rho_temp_3 = cudm_state::clone(*m_state); -// rho_temp_3 += (k3 * step_size); - -// cudm_state k4 = -// m_stepper->compute(rho_temp_3, m_t + step_size, step_size); - -// *m_state += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (step_size / 6.0); -// } else { -// throw std::runtime_error("Invalid integrator order"); -// } - -// // Update time -// m_t += step_size; -// } - -// // std::cout << "Integration complete. Final time: " << m_t << std::endl; -// } - -// // TODO: remove this -// runge_kutta_integrator::runge_kutta_integrator( -// cudm_state &&initial_state, double t0, -// std::shared_ptr stepper, int substeps) -// : m_t(t0), m_stepper(stepper), order(substeps) { - -// m_state = std::make_unique(std::move(initial_state)); -// } -// void runge_kutta_integrator::set_stepper( -// std::shared_ptr stepper) { -// m_stepper = stepper; -// } - -// void runge_kutta_integrator::set_state(cudm_state &&initial_state) { -// m_state = std::make_unique(std::move(initial_state)); -// m_t = 0.0; -// } } // namespace cudaq diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index ebf2c4750a..897470e6f8 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -47,7 +47,7 @@ TEST_F(CuDensityExpectationTest, checkCompute) { for (std::size_t stateIdx = 0; stateIdx < dims[0]; ++stateIdx) { std::vector> initialState(dims[0], 0.0); initialState[stateIdx] = 1.0; - auto inputState = std::make_unique(handle_, initialState, dims); + auto inputState = std::make_unique(handle_, initialState, dims); expectation.prepare(inputState->get_impl()); const auto expVal = expectation.compute(inputState->get_impl(), 0.0); EXPECT_NEAR(expVal.real(), 1.0 * stateIdx, 1e-12); @@ -75,7 +75,7 @@ TEST_F(CuDensityExpectationTest, checkCompositeSystem) { std::vector> initialState( initial_state_vec.data(), initial_state_vec.data() + initial_state_vec.size()); - auto inputState = std::make_unique(handle_, initialState, dims); + auto inputState = std::make_unique(handle_, initialState, dims); expectation.prepare(inputState->get_impl()); const auto expVal = expectation.compute(inputState->get_impl(), 0.0); std::cout << "Result: " << expVal << "\n"; diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 5c759f509d..f4eea77005 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -23,7 +23,7 @@ class CuDensityMatHelpersTestFixture : public ::testing::Test { cudensitymatHandle_t handle; cudaStream_t stream; std::unique_ptr helper; - std::unique_ptr state; + std::unique_ptr state; void SetUp() override { HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); @@ -32,7 +32,7 @@ class CuDensityMatHelpersTestFixture : public ::testing::Test { std::vector mode_extents = {2}; std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - state = std::make_unique(handle, rawData, mode_extents); + state = std::make_unique(handle, rawData, mode_extents); } void TearDown() override { HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); } @@ -44,7 +44,7 @@ TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - cudaq::cudm_state state(handle, rawData, mode_extents); + cudaq::CuDensityMatState state(handle, rawData, mode_extents); ASSERT_TRUE(state.is_initialized()); } diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 8178b229ea..f77e894cf8 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -51,7 +51,7 @@ class CuDensityMatStateTest : public ::testing::Test { }; TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { - cudm_state state(handle, stateVectorData, hilbertSpaceDims); + CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims); EXPECT_TRUE(state.is_initialized()); EXPECT_FALSE(state.is_density_matrix()); @@ -59,7 +59,7 @@ TEST_F(CuDensityMatStateTest, InitializeWithStateVector) { } TEST_F(CuDensityMatStateTest, InitializeWithDensityMatrix) { - cudm_state state(handle, densityMatrixData, hilbertSpaceDims); + CuDensityMatState state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_initialized()); EXPECT_TRUE(state.is_density_matrix()); @@ -70,22 +70,22 @@ TEST_F(CuDensityMatStateTest, InvalidInitialization) { // Data size mismatch for hilbertSpaceDims (2x2 system expects size 4 or 16) std::vector> invalidData = {{1.0, 0.0}, {0.0, 0.0}}; - EXPECT_THROW(cudm_state state(handle, invalidData, hilbertSpaceDims), + EXPECT_THROW(CuDensityMatState state(handle, invalidData, hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ToDensityMatrixConversion) { - cudm_state state(handle, stateVectorData, hilbertSpaceDims); + CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); - cudm_state densityMatrixState = state.to_density_matrix(); + CuDensityMatState densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); EXPECT_NO_THROW(densityMatrixState.dump(std::cout)); } TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { - cudm_state state(handle, densityMatrixData, hilbertSpaceDims); + CuDensityMatState state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); EXPECT_THROW(state.to_density_matrix(), std::runtime_error); @@ -93,24 +93,24 @@ TEST_F(CuDensityMatStateTest, AlreadyDensityMatrixConversion) { TEST_F(CuDensityMatStateTest, DestructorCleansUp) { EXPECT_NO_THROW( - { cudm_state state(handle, stateVectorData, hilbertSpaceDims); }); + { CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims); }); } TEST_F(CuDensityMatStateTest, InitializeWithEmptyRawData) { std::vector> emptyData; - EXPECT_THROW(cudm_state state(handle, emptyData, hilbertSpaceDims), + EXPECT_THROW(CuDensityMatState state(handle, emptyData, hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { hilbertSpaceDims = {2}; stateVectorData = {{1.0, 0.0}, {0.0, 0.0}}; - cudm_state state(handle, stateVectorData, hilbertSpaceDims); + CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims); EXPECT_FALSE(state.is_density_matrix()); - cudm_state densityMatrixState = state.to_density_matrix(); + CuDensityMatState densityMatrixState = state.to_density_matrix(); EXPECT_TRUE(densityMatrixState.is_density_matrix()); EXPECT_TRUE(densityMatrixState.is_initialized()); EXPECT_NO_THROW(densityMatrixState.dump(std::cout)); @@ -119,17 +119,17 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - EXPECT_THROW(cudm_state state(handle, stateVectorData, hilbertSpaceDims), + EXPECT_THROW(CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims), std::invalid_argument); } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { - cudm_state state(handle, densityMatrixData, hilbertSpaceDims); + CuDensityMatState state(handle, densityMatrixData, hilbertSpaceDims); EXPECT_TRUE(state.is_density_matrix()); EXPECT_TRUE(state.is_initialized()); } TEST_F(CuDensityMatStateTest, DumpWorksForInitializedState) { - cudm_state state(handle, stateVectorData, hilbertSpaceDims); + CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims); EXPECT_NO_THROW(state.dump(std::cout)); } diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index af3cfc8abf..2cb2caff80 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -84,8 +84,8 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepNoLiouvillian) { // Compute step with mismatched dimensions TEST_F(CuDensityMatTimeStepperTest, ComputeStepMistmatchedDimensions) { - EXPECT_THROW(std::unique_ptr mismatchedState = - std::make_unique(handle_, + EXPECT_THROW(std::unique_ptr mismatchedState = + std::make_unique(handle_, mock_initial_state_data(), std::vector{3, 3}), std::invalid_argument); diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 937875d36d..bd36537a89 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -22,7 +22,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { cudensitymatHandle_t handle_; cudensitymatOperator_t liouvillian_; std::unique_ptr integrator_; - std::unique_ptr state_; + std::unique_ptr state_; void SetUp() override { // Create library handle @@ -32,7 +32,7 @@ class RungeKuttaIntegratorTest : public ::testing::Test { liouvillian_ = mock_liouvillian(handle_); // Create initial state - state_ = std::make_unique(handle_, mock_initial_state_data(), + state_ = std::make_unique(handle_, mock_initial_state_data(), mock_hilbert_space_dims()); ASSERT_NE(state_, nullptr); ASSERT_TRUE(state_->is_initialized()); @@ -59,7 +59,7 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { // Integration with Euler Method (substeps = 1) TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // auto integrator = std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), + // CuDensityMatState(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), 0.0, time_stepper_, 1); // integrator->set_option("dt", 0.1); // EXPECT_NO_THROW(integrator->integrate(1.0)); @@ -68,7 +68,7 @@ TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { // Integration with Midpoint Rule (substeps = 2) TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { // auto midpointIntegrator = std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), + // CuDensityMatState(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); // midpointIntegrator->set_option("dt", 0.1); // EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); @@ -108,7 +108,7 @@ TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { // Missing Time Step (dt) TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { // auto integrator_missing_dt = std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), + // CuDensityMatState(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); // EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); @@ -140,7 +140,7 @@ TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { // Invalid Substeps TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { // EXPECT_THROW(std::make_unique( - // cudm_state(handle_, mock_initial_state_data(), + // CuDensityMatState(handle_, mock_initial_state_data(), // mock_hilbert_space_dims()), // 0.0, time_stepper_, 3), // std::invalid_argument); From 6f03515e978fc658cf4dce758ff77e275ec3b01e Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 17 Feb 2025 05:52:08 +0000 Subject: [PATCH 280/311] Add handle to sim class Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/CMakeLists.txt | 1 - runtime/nvqir/cudensitymat/CuDensityMatSim.cpp | 16 ++++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 5a3807c825..04886e9e5e 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -28,7 +28,6 @@ add_library(${LIBRARY_NAME} SHARED CuDensityMatSim.cpp mpi_support.cpp cudm_helpers.cpp - # cudm_state.cpp cudm_time_stepper.cpp runge_kutta_integrator.cpp cudm_expectation.cpp diff --git a/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp b/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp index 2ba56875c2..bae7ba125b 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp @@ -76,6 +76,9 @@ class CuDensityMatSim : public nvqir::CircuitSimulatorBase { using nvqir::CircuitSimulatorBase::shouldObserveFromSampling; using nvqir::CircuitSimulatorBase::summaryData; +private: + cudensitymatHandle_t m_cudmHandle = nullptr; + public: /// @brief The constructor CuDensityMatSim() { @@ -86,11 +89,12 @@ class CuDensityMatSim : public nvqir::CircuitSimulatorBase { if (cudaq::mpi::is_initialized()) initCuDensityMatCommLib(); HANDLE_CUDA_ERROR(cudaSetDevice(deviceId)); + HANDLE_CUDM_ERROR(cudensitymatCreate(&m_cudmHandle)); } /// The destructor - virtual ~CuDensityMatSim() = default; - + virtual ~CuDensityMatSim() { cudensitymatDestroy(m_cudmHandle); } + cudensitymatHandle_t getHandle() const { return m_cudmHandle; } std::unique_ptr getSimulationState() override { return std::make_unique(); } @@ -132,3 +136,11 @@ class CuDensityMatSim : public nvqir::CircuitSimulatorBase { } // namespace NVQIR_REGISTER_SIMULATOR(CuDensityMatSim, dynamics) + +extern "C" { +cudensitymatHandle_t getCudensitymatHandle() { + auto *sim = dynamic_cast(getCircuitSimulator_dynamics()); + assert(sim); + return sim->getHandle(); +} +} \ No newline at end of file From d1ddf7608600715f0463a20026cd1de4bee6b0ed Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Mon, 17 Feb 2025 07:09:15 +0000 Subject: [PATCH 281/311] Some fixes for _wrap_callback and add TODO Signed-off-by: Thien Nguyen --- runtime/cudaq/dynamics/schedule.cpp | 3 +- runtime/cudaq/schedule.h | 9 ++-- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 44 +++++++------------ .../nvqir/cudensitymat/cudm_time_stepper.cpp | 11 ++++- unittests/dynamics/test_cudm_time_stepper.cpp | 36 +++++++++++++++ 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/runtime/cudaq/dynamics/schedule.cpp b/runtime/cudaq/dynamics/schedule.cpp index 313d1322ca..1deb469748 100644 --- a/runtime/cudaq/dynamics/schedule.cpp +++ b/runtime/cudaq/dynamics/schedule.cpp @@ -16,7 +16,8 @@ namespace cudaq { Schedule::Schedule( const std::vector &steps, const std::vector ¶meters, - std::function(const std::string &, double)> + std::function( + const std::unordered_map> &)> value_function) : _steps(steps), _parameters(parameters), _value_function(value_function) {} } // namespace cudaq diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h index 4a7a62c06b..9be42f03e8 100644 --- a/runtime/cudaq/schedule.h +++ b/runtime/cudaq/schedule.h @@ -23,7 +23,8 @@ class Schedule { private: std::vector _steps; std::vector _parameters; - std::function(const std::string &, double)> + std::function( + const std::unordered_map> &)> _value_function; public: @@ -53,7 +54,8 @@ class Schedule { const std::vector ¶meters() const { return _parameters; } - std::function(const std::string &, double)> + std::function( + const std::unordered_map> &)> value_function() const { return _value_function; } @@ -70,7 +72,8 @@ class Schedule { /// _current_idx will be used to track the position in the sequence of steps. Schedule(const std::vector &steps, const std::vector ¶meters = {}, - std::function(const std::string &, double)> + std::function( + const std::unordered_map> &)> value_function = {}); }; } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 55e4c8a912..0be2e7b0d1 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -29,16 +29,6 @@ cudm_helper::~cudm_helper() { cudensitymatWrappedScalarCallback_t cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { - try { - std::complex evaluatedValue = scalar_op.evaluate({}); - - cudensitymatWrappedScalarCallback_t wrapped_callback; - wrapped_callback.callback = nullptr; - wrapped_callback.wrapper = new std::complex(evaluatedValue); - return wrapped_callback; - } catch (const std::exception &) { - } - if (scalar_op.is_constant()) { throw std::runtime_error( "scalar_operator does not have a valid generator function."); @@ -48,28 +38,26 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { cudaDataType_t data_type, void *scalar_storage) -> int32_t { try { - scalar_operator *scalar_op = - static_cast(scalar_storage); + std::cout << "Time = " << time << "\n"; + std::cout << "Params: "; + for (int i = 0; i < num_params; ++i) + std::cout << params[i] << " "; + std::cout << "\n"; + std::cout << "scalar_storage = " << scalar_storage << "\n"; std::unordered_map> param_map; // FIXME: Figure how to populate the param map + // This param map was sorted in cudmStepper::compute for (size_t i = 0; i < num_params; i++) { param_map[std::to_string(i)] = params[i]; } - - std::complex result = scalar_op->evaluate(param_map); - - if (data_type == CUDA_C_64F) { - *reinterpret_cast(scalar_storage) = - make_cuDoubleComplex(result.real(), result.imag()); - } else if (data_type == CUDA_C_32F) { - *reinterpret_cast(scalar_storage) = - make_cuFloatComplex(static_cast(result.real()), - static_cast(result.imag())); - } else { - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - + param_map["time"] = time; + // TODO: figure out how to capture the scalar_op. + // Note: cudensitymat expects a pure C function pointer, no state.... + // std::complex result = scalar_op.evaluate(param_map); + // std::cout << "Result = " << result << "\n"; + auto *tdCoef = static_cast *>(scalar_storage); + *tdCoef = 2.0; return CUDENSITYMAT_STATUS_SUCCESS; } catch (const std::exception &e) { std::cerr << "Error in scalar callback: " << e.what() << std::endl; @@ -79,8 +67,8 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { cudensitymatWrappedScalarCallback_t wrappedCallback; wrappedCallback.callback = callback; - wrappedCallback.wrapper = new scalar_operator(scalar_op); - + // Note: the wrapper must be nullptr + wrappedCallback.wrapper = nullptr; return wrappedCallback; } diff --git a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp index 61b7a1c35a..29938fb5f8 100644 --- a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp @@ -77,10 +77,17 @@ state cudmStepper::compute( } // Apply the operator action + std::map> sortedParameters( + parameters.begin(), parameters.end()); + std::vector paramValues; + for (const auto &[k, v] : sortedParameters) { + paramValues.emplace_back(v.real()); + paramValues.emplace_back(v.imag()); + } HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); HANDLE_CUDM_ERROR(cudensitymatOperatorComputeAction( - m_handle, m_liouvillian, t, 0, nullptr, state.get_impl(), - next_state.get_impl(), workspace, 0x0)); + m_handle, m_liouvillian, t, paramValues.size(), paramValues.data(), + state.get_impl(), next_state.get_impl(), workspace, 0x0)); HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); // Cleanup diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 2cb2caff80..b341b61676 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -167,3 +167,39 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudm_lindblad_op)); } + +TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { + const std::vector> initialState = { + {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + const std::vector dims = {4}; + auto inputState = cudaq::state::from_data(initialState); + auto *simState = cudaq::state_helper::getSimulationState(&inputState); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); + auto function = [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("alpha"); + if (entry == parameters.end()) + throw std::runtime_error("Cannot find time value"); + return entry->second; + }; + auto op = + cudaq::scalar_operator(function) * cudaq::matrix_operator::create(0); + auto cudmOp = + helper_->convert_to_cudensitymat_operator( + {}, op, dims); // Initialize the time stepper + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(inputState, 1.0, 1.0, {{"alpha", 2.0}}); + outputState.dump(std::cout); + std::vector> outputStateVec(4); + outputState.to_host(outputStateVec.data(), outputStateVec.size()); + // Create operator move the state up 1 step. + const std::vector> expectedOutputState = { + {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + + for (std::size_t i = 0; i < expectedOutputState.size(); ++i) { + EXPECT_TRUE(std::abs(expectedOutputState[i] - outputStateVec[i]) < 1e-12); + } + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); +} From 737c6672a0020d7fdb06b6a7abb42ada6caa179c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Fri, 14 Feb 2025 13:47:39 +0000 Subject: [PATCH 282/311] commit before switching branches Signed-off-by: Bettina Heim --- main.cpp | 57 ++++++++++++++++------------- old.perf | 76 ++++++++++++++++++++------------------- scripts/build_cudaq.sh | 2 +- v2.perf | 82 +++++++++++++++++++++--------------------- 4 files changed, 116 insertions(+), 101 deletions(-) diff --git a/main.cpp b/main.cpp index 4b062deb8e..faaceb984d 100644 --- a/main.cpp +++ b/main.cpp @@ -146,13 +146,16 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; - if (run_old && nr_reps <= 1000) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; + if (run_old) { + if (nr_reps > 1000) std::cout << "Old setup takes minutes - skipped" << std::endl; + else { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } } if (run_new) { @@ -175,13 +178,16 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; - if (run_old && nr_reps <= 1000) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; + if (run_old) { + if (nr_reps > 1000) std::cout << "Old setup takes minutes - skipped" << std::endl; + else { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } } if (run_new) { @@ -311,16 +317,19 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; - if (run_old && nr_reps <= 20) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; + if (run_old) { + if (nr_reps >= 30) std::cout << "Old setup is killed (out of memory?) - skipped" << std::endl; + else { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } } - if (run_new && nr_reps <= 20) { + if (run_new) { auto start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); @@ -341,7 +350,7 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; - if (run_old && nr_reps <= 20) { + if (run_old) { /* start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) @@ -353,7 +362,7 @@ int main() { std::cout << "Old setup segfaults" << std::endl; } - if (run_new && nr_reps <= 20) { + if (run_new) { auto start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) op_sum *= (cudaq::spin_operator::x(nr_reps - i) + cudaq::spin_operator::z(nr_reps - i - 1)); diff --git a/old.perf b/old.perf index e78b18971c..f7fb42b449 100644 --- a/old.perf +++ b/old.perf @@ -1,80 +1,83 @@ multiplication inplace with itself nr ops: 100 -Old setup took 0.000989452 seconds. +Old setup took 0.000145484 seconds. nr ops: 1000 -Old setup took 0.00629858 seconds. +Old setup took 0.0010659 seconds. nr ops: 10000 -Old setup took 0.0611419 seconds. +Old setup took 0.00943221 seconds. multiplication inplace with other nr ops: 100 -Old setup took 0.00300721 seconds. +Old setup took 0.000267917 seconds. nr ops: 1000 -Old setup took 0.200118 seconds. +Old setup took 0.00509327 seconds. nr ops: 10000 -Old setup took 18.4632 seconds. +Old setup took 0.402641 seconds. multiplication inplace with other (reverse order) nr ops: 100 -Old setup took 0.00385455 seconds. +Old setup took 0.000178547 seconds. nr ops: 1000 -Old setup took 0.26617 seconds. +Old setup took 0.00640537 seconds. nr ops: 10000 -Old setup took 26.2626 seconds. +Old setup took 0.558541 seconds. addition inplace with itself nr ops: 100 -Old setup took 0.000757876 seconds. +Old setup took 7.7252e-05 seconds. nr ops: 1000 -Old setup took 0.00587647 seconds. +Old setup took 0.00067837 seconds. nr ops: 10000 -Old setup took 0.059795 seconds. +Old setup took 0.00674406 seconds. addition inplace with other nr ops: 100 -Old setup took 0.0500884 seconds. +Old setup took 0.00211015 seconds. nr ops: 1000 -Old setup took 19.3641 seconds. +Old setup took 0.765903 seconds. nr ops: 10000 +Old setup takes minutes - skipped addition inplace with other (reverse order) nr ops: 100 -Old setup took 0.0191391 seconds. +Old setup took 0.000765763 seconds. nr ops: 1000 -Old setup took 15.3959 seconds. +Old setup took 0.526556 seconds. nr ops: 10000 +Old setup takes minutes - skipped addition inplace with product self nr ops: 100 -Old setup took 0.00115114 seconds. +Old setup took 8.0988e-05 seconds. nr ops: 1000 -Old setup took 0.0128529 seconds. +Old setup took 0.000713935 seconds. nr ops: 10000 -Old setup took 0.126899 seconds. +Old setup took 0.00660016 seconds. addition inplace with product self (reverse order) nr ops: 100 -Old setup took 0.002065 seconds. +Old setup took 7.1369e-05 seconds. nr ops: 1000 -Old setup took 0.0172566 seconds. +Old setup took 0.000466354 seconds. nr ops: 10000 -Old setup took 0.123577 seconds. +Old setup took 0.00452788 seconds. product inplace with 2-term sum on fixed degrees nr ops: 100 -Old setup took 0.00136818 seconds. +Old setup took 0.000101042 seconds. nr ops: 1000 -Old setup took 0.0159856 seconds. +Old setup took 0.000903594 seconds. nr ops: 10000 -Old setup took 0.140271 seconds. +Old setup took 0.0091517 seconds. product inplace with 2-term sum on varying degrees nr ops: 10 -Old setup took 0.0362399 seconds. +Old setup took 0.0103578 seconds. nr ops: 20 -Old setup took 11.1247 seconds. +Old setup took 1.60473 seconds. nr ops: 30 +Old setup is killed (out of memory?) - skipped product inplace with 2-term sum on varying degrees (reverse order) nr ops: 10 @@ -82,23 +85,24 @@ Old setup segfaults nr ops: 20 Old setup segfaults nr ops: 30 +Old setup segfaults sum of products of random terms nr terms 10, term length 100 -Old setup took 0.0286609 seconds. +Old setup took 0.00225446 seconds. nr terms 10, term length 1000 -Old setup took 0.30442 seconds. +Old setup took 0.0125959 seconds. nr terms 10, term length 10000 -Old setup took 3.1964 seconds. +Old setup took 0.121102 seconds. nr terms 100, term length 100 -Old setup took 0.319011 seconds. +Old setup took 0.0117404 seconds. nr terms 100, term length 1000 -Old setup took 3.07269 seconds. +Old setup took 0.116341 seconds. nr terms 100, term length 10000 -Old setup took 30.7527 seconds. +Old setup took 1.17402 seconds. nr terms 1000, term length 100 -Old setup took 3.64059 seconds. +Old setup took 0.12738 seconds. nr terms 1000, term length 1000 -Old setup took 31.0172 seconds. +Old setup took 1.17435 seconds. nr terms 1000, term length 10000 -Old setup took 311.449 seconds. +Old setup took 11.5931 seconds. diff --git a/scripts/build_cudaq.sh b/scripts/build_cudaq.sh index 1384620bcc..95cbf387e0 100644 --- a/scripts/build_cudaq.sh +++ b/scripts/build_cudaq.sh @@ -73,7 +73,7 @@ repo_root=$(cd "$this_file_dir" && git rev-parse --show-toplevel) # Prepare the build directory mkdir -p "$CUDAQ_INSTALL_PREFIX/bin" -mkdir -p "$working_dir/build" && cd "$working_dir/build" && rm -rf * +mkdir -p "$working_dir/build" && cd "$working_dir/build" # && rm -rf * mkdir -p logs && rm -rf logs/* if [ -n "$install_toolchain" ]; then diff --git a/v2.perf b/v2.perf index 7ed7efe5b9..3bb78ca93b 100644 --- a/v2.perf +++ b/v2.perf @@ -1,106 +1,108 @@ multiplication inplace with itself nr ops: 100 -New setup took 0.00101147 seconds. +New setup took 6.1922e-05 seconds. nr ops: 1000 -New setup took 0.00690629 seconds. +New setup took 0.000226954 seconds. nr ops: 10000 -New setup took 0.0446706 seconds. +New setup took 0.00221656 seconds. multiplication inplace with other nr ops: 100 -New setup took 0.00393387 seconds. +New setup took 0.000310864 seconds. nr ops: 1000 -New setup took 0.284029 seconds. +New setup took 0.0201444 seconds. nr ops: 10000 -New setup took 27.0573 seconds. +New setup took 2.35453 seconds. multiplication inplace with other (reverse order) nr ops: 100 -New setup took 0.00152094 seconds. +New setup took 9.8062e-05 seconds. nr ops: 1000 -New setup took 0.201935 seconds. +New setup took 0.00733987 seconds. nr ops: 10000 -New setup took 9.19236 seconds. +New setup took 0.787652 seconds. addition inplace with itself nr ops: 100 -New setup took 0.000482909 seconds. +New setup took 3.5232e-05 seconds. nr ops: 1000 -New setup took 0.00343451 seconds. +New setup took 0.00020187 seconds. nr ops: 10000 -New setup took 0.0370845 seconds. +New setup took 0.00228643 seconds. addition inplace with other nr ops: 100 -New setup took 0.00056023 seconds. +New setup took 6.3769e-05 seconds. nr ops: 1000 -New setup took 0.0047066 seconds. +New setup took 0.000345559 seconds. nr ops: 10000 -New setup took 0.0604502 seconds. +New setup took 0.00422566 seconds. addition inplace with other (reverse order) nr ops: 100 -New setup took 0.000840673 seconds. +New setup took 0.00023781 seconds. nr ops: 1000 -New setup took 0.00583381 seconds. +New setup took 0.000287685 seconds. nr ops: 10000 -New setup took 0.0606676 seconds. +New setup took 0.00293226 seconds. addition inplace with product self nr ops: 100 -New setup took 0.00156973 seconds. +New setup took 0.000141212 seconds. nr ops: 1000 -New setup took 0.0138318 seconds. +New setup took 0.0012408 seconds. nr ops: 10000 -New setup took 0.114067 seconds. +New setup took 0.0103075 seconds. addition inplace with product self (reverse order) nr ops: 100 -New setup took 0.00111965 seconds. +New setup took 0.000147012 seconds. nr ops: 1000 -New setup took 0.0144028 seconds. +New setup took 0.00126523 seconds. nr ops: 10000 -New setup took 0.138953 seconds. +New setup took 0.0106776 seconds. product inplace with 2-term sum on fixed degrees nr ops: 100 -New setup took 0.0124617 seconds. +New setup took 0.000316067 seconds. nr ops: 1000 -New setup took 0.0745379 seconds. +New setup took 0.00306611 seconds. nr ops: 10000 -New setup took 0.585963 seconds. +New setup took 0.0314979 seconds. product inplace with 2-term sum on varying degrees nr ops: 10 -New setup took 0.0314756 seconds. +New setup took 0.0025696 seconds. nr ops: 20 -New setup took 60.9737 seconds. +New setup took 6.56947 seconds. nr ops: 30 +New setup is killed (out of memory?) - skipped product inplace with 2-term sum on varying degrees (reverse order) nr ops: 10 -New setup took 0.0289655 seconds. +New setup took 0.00195102 seconds. nr ops: 20 -New setup took 49.7242 seconds. +New setup took 4.47496 seconds. nr ops: 30 +New setup is killed (out of memory?) - skipped sum of products of random terms nr terms 10, term length 100 -New setup took 0.0107945 seconds. +New setup took 0.00114159 seconds. nr terms 10, term length 1000 -New setup took 0.192907 seconds. +New setup took 0.0144861 seconds. nr terms 10, term length 10000 -New setup took 2.04548 seconds. +New setup took 0.15289 seconds. nr terms 100, term length 100 -New setup took 0.117314 seconds. +New setup took 0.0102112 seconds. nr terms 100, term length 1000 -New setup took 1.93779 seconds. +New setup took 0.167844 seconds. nr terms 100, term length 10000 -New setup took 20.3838 seconds. +New setup took 1.50191 seconds. nr terms 1000, term length 100 -New setup took 1.14292 seconds. +New setup took 0.0975547 seconds. nr terms 1000, term length 1000 -New setup took 19.3147 seconds. +New setup took 1.50753 seconds. nr terms 1000, term length 10000 -New setup took 204.79 seconds. +New setup took 15.371 seconds. From b6824309871c4b9b4fa8d201a262b6ac90067de9 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 17 Feb 2025 18:58:12 +0000 Subject: [PATCH 283/311] third and final version with a decent performance across the board Signed-off-by: Bettina Heim --- main.cpp | 17 +- old.perf | 4 - runtime/cudaq/dynamics/boson_operators.cpp | 20 +- runtime/cudaq/dynamics/boson_operators.h | 12 +- runtime/cudaq/dynamics/matrix_operators.cpp | 54 +-- runtime/cudaq/dynamics/matrix_operators.h | 3 +- runtime/cudaq/dynamics/operator_leafs.h | 4 +- runtime/cudaq/dynamics/operator_sum.cpp | 485 ++++++++++--------- runtime/cudaq/dynamics/product_operators.cpp | 387 +++++++-------- runtime/cudaq/dynamics/scalar_operators.cpp | 8 + runtime/cudaq/dynamics/spin_operators.cpp | 16 +- runtime/cudaq/dynamics/spin_operators.h | 7 +- runtime/cudaq/operators.h | 51 +- scripts/build_cudaq.sh | 4 +- v1.perf | 82 ++-- v3.perf | 104 ++++ 16 files changed, 692 insertions(+), 566 deletions(-) create mode 100644 v3.perf diff --git a/main.cpp b/main.cpp index faaceb984d..b444c33d4a 100644 --- a/main.cpp +++ b/main.cpp @@ -303,7 +303,7 @@ int main() { - repetitions = {10, 20, 30}; + repetitions = {10, 20}; // we run out of memory if we go to 30 here // product inplace with 2-term sum on varying degrees @@ -318,15 +318,12 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - if (nr_reps >= 30) std::cout << "Old setup is killed (out of memory?) - skipped" << std::endl; - else { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; } if (run_new) { diff --git a/old.perf b/old.perf index f7fb42b449..4f2eca95d3 100644 --- a/old.perf +++ b/old.perf @@ -76,16 +76,12 @@ nr ops: 10 Old setup took 0.0103578 seconds. nr ops: 20 Old setup took 1.60473 seconds. -nr ops: 30 -Old setup is killed (out of memory?) - skipped product inplace with 2-term sum on varying degrees (reverse order) nr ops: 10 Old setup segfaults nr ops: 20 Old setup segfaults -nr ops: 30 -Old setup segfaults sum of products of random terms nr terms 10, term length 100 diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 6a3560ac47..81fc5af12c 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -16,6 +16,11 @@ namespace cudaq { +// FIXME: GET RID OF THIS AND INSTEAD MAKE SURE WE AGGREGATE TERMS PROPERLY +#if !defined(NDEBUG) +bool boson_operator::can_be_canonicalized = false; +#endif + // private helpers std::string boson_operator::op_code_to_string() const { @@ -31,19 +36,20 @@ std::string boson_operator::op_code_to_string() const { // read-only properties -const std::string &boson_operator::unique_id() const { return this->id; } +std::string boson_operator::unique_id() const { + return this->op_code_to_string() + std::to_string(target); +} std::vector boson_operator::degrees() const { return {this->target}; } // constructors -boson_operator::boson_operator(int target) - : op_code(0), target(target), id("I" + std::to_string(target)) {} +boson_operator::boson_operator(int target) + : op_code(0), target(target) {} -boson_operator::boson_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); - this->id = this->op_code_to_string() + std::to_string(target); +boson_operator::boson_operator(int target, int op_id) + : op_code(op_id), target(target) { + assert(0 <= op_id < 4); } // evaluations diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index ece9113129..55e39499b9 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -21,22 +21,28 @@ template class product_operator; // FIXME: rename? -class boson_operator : public operator_handler { +class boson_operator : public operator_handler{ +template friend class product_operator; private: // 0 = I, 1 = Ad (create), 2 = A (annihilate), 3 = AdA (number) int op_code; int target; - std::string id; boson_operator(int target, int op_code); std::string op_code_to_string() const; public: + +// FIXME: GET RID OF THIS +#if !defined(NDEBUG) + static bool can_be_canonicalized; // needs to be false; no canonical order can be defined for matrix operator expressions +#endif + // read-only properties - virtual const std::string &unique_id() const; + virtual std::string unique_id() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 576dac75ab..46432073ea 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -76,14 +76,20 @@ matrix_operator::instantiate(std::string operator_id, // read-only properties -const std::string &matrix_operator::unique_id() const { return this->id; } +std::string matrix_operator::unique_id() const { + auto it = this->targets.cbegin(); + auto str = this->op_code + std::to_string(*it); + while (++it != this->targets.cend()) + str += "." + std::to_string(*it); + return str; +} std::vector matrix_operator::degrees() const { return this->targets; } // constructors matrix_operator::matrix_operator(int degree) { - std::string op_code = "identity"; + std::string op_code = "I"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { auto func = [](const std::vector &dimensions, @@ -101,35 +107,23 @@ matrix_operator::matrix_operator(int degree) { } this->op_code = op_code; this->targets.push_back(degree); - this->id = "I"; - for (auto t : this->targets) - this->id += std::to_string(t); } -matrix_operator::matrix_operator(std::string operator_id, - const std::vector °rees) - : op_code(operator_id), targets(degrees), id(operator_id) { - assert(this->targets.size() > 0); - for (auto t : this->targets) - this->id += std::to_string(t); -} +matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) + : op_code(operator_id), targets(degrees) { + assert(this->targets.size() > 0); + } -matrix_operator::matrix_operator(std::string operator_id, - std::vector &°rees) - : op_code(operator_id), targets(std::move(degrees)), id(operator_id) { - assert(this->targets.size() > 0); - for (auto t : this->targets) - this->id += std::to_string(t); -} +matrix_operator::matrix_operator(std::string operator_id, std::vector &°rees) + : op_code(operator_id), targets(std::move(degrees)) { + assert(this->targets.size() > 0); + } template , bool>> matrix_operator::matrix_operator(const T &other) { - std::string type_prefix = matrix_operator::type_prefix(); this->targets = other.degrees(); - this->op_code = type_prefix + other.to_string(false) + - std::to_string(this->targets.size()); - this->id = type_prefix + other.unique_id(); + this->op_code = matrix_operator::type_prefix() + other.to_string(false); if (matrix_operator::m_ops.find(this->op_code) == matrix_operator::m_ops.end()) { auto func = [targets = other.degrees(), other] (const std::vector &dimensions, const std::unordered_map> &_none) { @@ -150,10 +144,10 @@ template matrix_operator::matrix_operator(const spin_operator &other); template matrix_operator::matrix_operator(const boson_operator &other); matrix_operator::matrix_operator(const matrix_operator &other) - : targets(other.targets), op_code(other.op_code), id(other.id) {} + : targets(other.targets), op_code(other.op_code) {} -matrix_operator::matrix_operator(matrix_operator &&other) - : targets(std::move(other.targets)), op_code(other.op_code), id(other.id) {} +matrix_operator::matrix_operator(matrix_operator &&other) + : targets(std::move(other.targets)), op_code(other.op_code) {} // assignments @@ -161,7 +155,6 @@ matrix_operator &matrix_operator::operator=(const matrix_operator &other) { if (this != &other) { this->targets = other.targets; this->op_code = other.op_code; - this->id = other.id; } return *this; } @@ -184,7 +177,6 @@ matrix_operator &matrix_operator::operator=(matrix_operator &&other) { if (this != &other) { this->targets = std::move(other.targets); this->op_code = other.op_code; - this->id = other.id; } return *this; } @@ -229,9 +221,9 @@ std::string matrix_operator::to_string(bool include_degrees) const { else if (this->targets.size() == 0) return this->op_code + "()"; auto it = this->targets.cbegin(); - std::string str = this->op_code + "(" + std::to_string(*it++); - while (it != this->targets.cend()) - str += ", " + std::to_string(*it++); + std::string str = this->op_code + "(" + std::to_string(*it); + while (++it != this->targets.cend()) + str += ", " + std::to_string(*it); return str + ")"; } diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index cf3ef41581..5c355e282f 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -32,7 +32,6 @@ class matrix_operator : public operator_handler { protected: std::vector targets; std::string op_code; - std::string id; matrix_operator(std::string operator_id, const std::vector °rees); matrix_operator(std::string operator_id, std::vector &°rees); @@ -92,7 +91,7 @@ class matrix_operator : public operator_handler { // read-only properties - virtual const std::string &unique_id() const; + virtual std::string unique_id() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h index ee82e9e164..d35bc74768 100644 --- a/runtime/cudaq/dynamics/operator_leafs.h +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -72,6 +72,8 @@ class scalar_operator { matrix_2 to_matrix(const std::unordered_map> ¶meters = {}) const; + std::string to_string() const; + // comparisons bool operator==(scalar_operator other) const; @@ -152,7 +154,7 @@ class operator_handler { virtual ~operator_handler() = default; - virtual const std::string &unique_id() const = 0; + virtual std::string unique_id() const = 0; virtual std::vector degrees() const = 0; diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 740cc023c2..00c3c70d24 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -22,24 +22,30 @@ namespace cudaq { // private methods -template -void operator_sum::insert(product_operator &&other) { - auto term_id = other.term_id; // need to copy string since both the operator - // and the key need it - auto it = this->tmap.find(term_id); - if (it == this->tmap.end()) - this->tmap.insert(std::make_pair(term_id, std::move(other))); - else - it->second.coefficient += other.coefficient; +template +void operator_sum::insert(const product_operator &other) { + auto term_id = other.get_term_id(); + auto it = this->term_map.find(term_id); + if (it == this->term_map.cend()) { + this->coefficients.push_back(other.coefficient); + this->term_map.insert(it, std::make_pair(std::move(term_id), this->terms.size())); + this->terms.push_back(other.operators); + } else { + this->coefficients[it->second] += other.coefficient; + } } -template -void operator_sum::insert(const product_operator &other) { - auto it = this->tmap.find(other.term_id); - if (it == this->tmap.end()) - this->tmap.insert(std::make_pair(other.term_id, other)); - else - it->second.coefficient += other.coefficient; +template +void operator_sum::insert(product_operator &&other) { + auto term_id = other.get_term_id(); + auto it = this->term_map.find(term_id); + if (it == this->term_map.cend()) { + this->coefficients.push_back(std::move(other.coefficient)); + this->term_map.insert(it, std::make_pair(std::move(term_id), this->terms.size())); + this->terms.push_back(std::move(other.operators)); + } else { + this->coefficients[it->second] += other.coefficient; + } } template @@ -62,24 +68,21 @@ operator_sum::m_evaluate(MatrixArithmetics arithmetics, auto degrees = this->degrees(); // We need to make sure all matrices are of the same size to sum them up. - auto paddedTerm = [&arithmetics, °rees = std::as_const(degrees)]( - product_operator &&term) { - std::vector prod_ops; - prod_ops.reserve(degrees.size()); - auto term_degrees = term.degrees(); - std::string term_id = ""; - for (auto degree : degrees) { - auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); - if (it == term_degrees.end()) { - HandlerTy identity(degree); - term_id += identity.unique_id(); - prod_ops.push_back(std::move(identity)); + auto paddedTerm = + [&arithmetics, °rees = std::as_const(degrees)](product_operator &&term) { + std::vector prod_ops; + prod_ops.reserve(degrees.size()); + auto term_degrees = term.degrees(); + for (auto degree : degrees) { + auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); + if (it == term_degrees.end()) { + HandlerTy identity(degree); + prod_ops.push_back(std::move(identity)); + } } - } - product_operator prod(1, std::move(prod_ops), - std::move(term_id)); - prod *= term; // ensures canonical ordering - return prod; + product_operator prod(1, std::move(prod_ops)); + prod *= term; // ensures canonical ordering + return prod; }; if (pad_terms) { @@ -127,8 +130,8 @@ INSTANTIATE_SUM_PRIVATE_METHODS(boson_operator); template std::vector operator_sum::degrees() const { std::set unsorted_degrees; - for (const auto &entry : this->tmap) { - for (const HandlerTy &op : entry.second.operators) { + for (const std::vector &term : this->terms) { + for (const HandlerTy &op : term) { auto op_degrees = op.degrees(); unsorted_degrees.insert(op_degrees.cbegin(), op_degrees.cend()); } @@ -136,32 +139,35 @@ std::vector operator_sum::degrees() const { auto degrees = std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); cudaq::detail::canonicalize_degrees(degrees); - return degrees; + return std::move(degrees); } -template -int operator_sum::num_terms() const { - return this->tmap.size(); +template +int operator_sum::num_terms() const { + return this->terms.size(); } template std::vector> operator_sum::get_terms() const { std::vector> prods; - prods.reserve(this->tmap.size()); - for (const auto &entry : this->tmap) { - prods.push_back(entry.second); + prods.reserve(this->terms.size()); + for (size_t i = 0; i < this->terms.size(); ++i) { + prods.push_back(product_operator(this->coefficients[i], this->terms[i])); } - return prods; + return std::move(prods); } -#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ - \ - template std::vector operator_sum::degrees() const; \ - \ - template int operator_sum::num_terms() const; \ - \ - template std::vector> \ - operator_sum::get_terms() const; + +#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ + \ + template \ + std::vector operator_sum::degrees() const; \ + \ + template \ + int operator_sum::num_terms() const; \ + \ + template \ + std::vector> operator_sum::get_terms() const; INSTANTIATE_SUM_PROPERTIES(matrix_operator); INSTANTIATE_SUM_PROPERTIES(spin_operator); @@ -171,77 +177,82 @@ INSTANTIATE_SUM_PROPERTIES(boson_operator); template operator_sum::operator_sum(const product_operator &prod) { - this->tmap.max_load_factor(0.25); this->insert(prod); } template template, Args>...>::value, bool>> operator_sum::operator_sum(Args&&... args) { - this->tmap.max_load_factor(0.25); - this->tmap.reserve(sizeof...(Args)); - aggregate_terms(std::forward &&>(args)...); + this->coefficients.reserve(sizeof...(Args)); + this->term_map.reserve(sizeof...(Args)); + this->terms.reserve(sizeof...(Args)); + aggregate_terms(std::forward&&>(args)...); } template -operator_sum::operator_sum(std::vector> &&terms) { - this->tmap.max_load_factor(0.25); - this->tmap.reserve(terms.size()); - for (auto &&term : terms) - this->insert(std::move(term)); -} - -template -template ::value && - std::is_constructible::value, - bool>> -operator_sum::operator_sum(const operator_sum &other) { - this->tmap.max_load_factor(0.25); - this->tmap.reserve(other.tmap.size()); - for (const auto &entry : other.tmap) { - product_operator prod(entry.second); - this->insert(std::move(prod)); +template::value && std::is_constructible::value, bool>> +operator_sum::operator_sum(const operator_sum &other) +: coefficients(other.coefficients) { + this->term_map.reserve(other.terms.size()); + this->terms.reserve(other.terms.size()); + for (const auto &operators : other.terms) { + product_operator term(product_operator(1., operators)); // coefficient does not matter + this->term_map.insert(this->term_map.cend(), std::make_pair(term.get_term_id(), this->terms.size())); + this->terms.push_back(std::move(term.operators)); } } -template -operator_sum::operator_sum(const operator_sum &other) - : tmap(other.tmap) { - this->tmap.max_load_factor(0.25); +template +operator_sum::operator_sum(const operator_sum &other, int size) { + if (size <= 0) { + this->coefficients = other.coefficients; + this->term_map = other.term_map; + this->terms = other.terms; + } else { + this->coefficients.reserve(size); + this->term_map.reserve(size); + this->terms.reserve(size); + for (const auto &coeff : other.coefficients) + this->coefficients.push_back(coeff); + for (const auto &entry : other.term_map) + this->term_map.insert(this->term_map.cend(), entry); + for (const auto &term : other.terms) + this->terms.push_back(term); } +} template -operator_sum::operator_sum(operator_sum &&other) - : tmap(std::move(other.tmap)) { - this->tmap.max_load_factor(0.25); // probably not needed +operator_sum::operator_sum(operator_sum &&other, int size) +: coefficients(std::move(other.coefficients)), term_map(std::move(other.term_map)), terms(std::move(other.terms)) { + if (size > 0) { + this->coefficients.reserve(size); + this->term_map.reserve(size); + this->terms.reserve(size); } +} -#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ - \ - template operator_sum::operator_sum( \ - const product_operator &item2); \ - \ - template operator_sum::operator_sum( \ - product_operator &&item2); \ - \ - template operator_sum::operator_sum( \ - product_operator &&item1, \ - product_operator &&item2); \ - \ - template operator_sum::operator_sum( \ - product_operator &&item1, \ - product_operator &&item2, \ - product_operator &&item3); \ - \ - template operator_sum::operator_sum( \ - std::vector> &&terms); \ - \ - template operator_sum::operator_sum( \ - const operator_sum &other); \ - \ - template operator_sum::operator_sum( \ - operator_sum &&other); +#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ + \ + template \ + operator_sum::operator_sum(const product_operator &item2); \ + \ + template \ + operator_sum::operator_sum(product_operator &&item2); \ + \ + template \ + operator_sum::operator_sum(product_operator &&item1, \ + product_operator &&item2); \ + \ + template \ + operator_sum::operator_sum(product_operator &&item1, \ + product_operator &&item2, \ + product_operator &&item3); \ + \ + template \ + operator_sum::operator_sum(const operator_sum &other, int size); \ + \ + template \ + operator_sum::operator_sum(operator_sum &&other, int size); template operator_sum::operator_sum( const operator_sum &other); @@ -254,32 +265,32 @@ INSTANTIATE_SUM_CONSTRUCTORS(boson_operator); // assignments -template -template ::value && - std::is_constructible::value, - bool>> -operator_sum & -operator_sum::operator=(const product_operator &other) { - this->tmap.clear(); - product_operator prod(other); - this->insert(std::move(prod)); +template + template::value && std::is_constructible::value, bool>> +operator_sum& operator_sum::operator=(const product_operator &other) { + *this = product_operator(other); return *this; } -template -operator_sum & -operator_sum::operator=(product_operator &&other) { - this->tmap.clear(); - this->insert(std::move(other)); +template +operator_sum& operator_sum::operator=(const product_operator &other) { + this->coefficients.clear(); + this->term_map.clear(); + this->terms.clear(); + this->coefficients.push_back(other.coefficient); + this->term_map.insert(this->term_map.cend(), std::make_pair(other.get_term_id(), 0)); + this->terms.push_back(other.operators); return *this; } -template -operator_sum & -operator_sum::operator=(const product_operator &other) { - this->tmap.clear(); - this->insert(other); +template +operator_sum& operator_sum::operator=(product_operator &&other) { + this->coefficients.clear(); + this->term_map.clear(); + this->terms.clear(); + this->coefficients.push_back(std::move(other.coefficient)); + this->term_map.insert(this->term_map.cend(), std::make_pair(other.get_term_id(), 0)); + this->terms.push_back(std::move(other.operators)); return *this; } @@ -298,8 +309,9 @@ template operator_sum & operator_sum::operator=(const operator_sum &other) { if (this != &other) { - this->tmap = other.tmap; - this->tmap.max_load_factor(0.25); // probably not needed + this->coefficients = other.coefficients; + this->term_map = other.term_map; + this->terms = other.terms; } return *this; } @@ -308,8 +320,9 @@ template operator_sum & operator_sum::operator=(operator_sum &&other) { if (this != &other) { - this->tmap = std::move(other.tmap); - this->tmap.max_load_factor(0.25); // probably not needed + this->coefficients = std::move(other.coefficients); + this->term_map = std::move(other.term_map); + this->terms = std::move(other.terms); } return *this; } @@ -349,19 +362,18 @@ INSTANTIATE_SUM_ASSIGNMENTS(boson_operator); template std::string operator_sum::to_string() const { - auto it = this->tmap.cbegin(); - std::string str = it->second.to_string(); - while (++it != this->tmap.cend()) - str += " + " + it->second.to_string(); + auto prods = this->get_terms(); + auto it = prods.cbegin(); + std::string str = it->to_string(); + while (++it != prods.cend()) + str += " + " + it->to_string(); return std::move(str); } -template -matrix_2 operator_sum::to_matrix( - std::unordered_map dimensions, - const std::unordered_map> ¶meters) - const { - return m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); +template +matrix_2 operator_sum::to_matrix(std::unordered_map dimensions, + const std::unordered_map> ¶meters) const { + return std::move(m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix()); } #define INSTANTIATE_SUM_EVALUATIONS(HandlerTy) \ @@ -381,13 +393,19 @@ INSTANTIATE_SUM_EVALUATIONS(boson_operator); template operator_sum operator_sum::operator-() const & { - return *this * -1.; + operator_sum sum; + sum.coefficients.reserve(this->coefficients.size()); + sum.term_map = this->term_map; + sum.terms = this->terms; + for (auto &coeff : this->coefficients) + sum.coefficients.push_back(-1. * coeff); + return sum; } template operator_sum operator_sum::operator-() && { - for (auto &entry : this->tmap) - entry.second.coefficient *= -1; + for (auto &coeff : this->coefficients) + coeff *= -1.; return std::move(*this); } @@ -426,9 +444,11 @@ INSTANTIATE_SUM_UNARY_OPS(boson_operator); template \ operator_sum operator_sum::operator*(otherTy other) const & { \ operator_sum sum; \ - sum.tmap.reserve(this->tmap.size()); \ - for (const auto &entry : this->tmap) \ - sum.insert(other * entry.second); \ + sum.coefficients.reserve(this->coefficients.size()); \ + sum.term_map = this->term_map; \ + sum.terms = this->terms; \ + for (const auto &coeff : this->coefficients) \ + sum.coefficients.push_back(coeff * other); \ return sum; \ } \ \ @@ -445,9 +465,9 @@ SUM_MULTIPLICATION(const scalar_operator &); \ template \ operator_sum operator_sum::operator op(otherTy other) const & { \ - operator_sum sum(*this); \ + operator_sum sum(*this, this->terms.size() + 1); \ sum.insert(product_operator(op other)); \ - return sum; \ + return std::move(sum); \ } \ \ template \ @@ -508,10 +528,19 @@ INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template operator_sum operator_sum::operator*(const product_operator &other) const & { operator_sum sum; - sum.tmap.reserve(this->tmap.size()); - for (const auto &entry : this->tmap) - sum.insert(entry.second * other); - return sum; + sum.coefficients.reserve(this->coefficients.size()); + sum.term_map.reserve(this->terms.size()); + sum.terms.reserve(this->terms.size()); + for (auto i = 0; i < this->terms.size(); ++i) { + product_operator prod(this->coefficients[i] * other.coefficient); + prod.operators.reserve(this->terms[i].size() + other.operators.size()); + for (const auto &op : this->terms[i]) + prod.operators.push_back(op); // no need to insert here - should be canonicalized already + for (HandlerTy op : other.operators) + prod.insert(std::move(op)); + sum.insert(std::move(prod)); + } + return std::move(sum); } template @@ -524,9 +553,9 @@ operator_sum operator_sum::operator*(const product_operato template \ operator_sum operator_sum::operator op( \ const product_operator &other) const & { \ - operator_sum sum(*this); \ + operator_sum sum(*this, this->terms.size() + 1); \ sum.insert(op other); \ - return sum; \ + return std::move(sum); \ } \ \ template \ @@ -541,10 +570,20 @@ SUM_ADDITION_PRODUCT(-) template operator_sum operator_sum::operator*(const operator_sum &other) const & { operator_sum sum; - sum.tmap.reserve(this->tmap.size() * other.tmap.size()); - for (const auto &entry_self : this->tmap) { - for (const auto &entry_other : other.tmap) - sum.insert(entry_self.second * entry_other.second); + auto max_size = this->terms.size() * other.terms.size(); + sum.coefficients.reserve(max_size); + sum.term_map.reserve(max_size); + sum.terms.reserve(max_size); + for (auto i = 0; i < this->terms.size(); ++i) { + for (auto j = 0; j < other.terms.size(); ++j) { + product_operator prod(this->coefficients[i] * other.coefficients[j]); + prod.operators.reserve(this->terms[i].size() + other.terms[j].size()); + for (const auto &op : this->terms[i]) + prod.operators.push_back(op); // no need to insert here - should be canonicalized already + for (HandlerTy op : other.terms[j]) + prod.insert(std::move(op)); + sum.insert(std::move(prod)); + } } return sum; } @@ -559,14 +598,14 @@ operator_sum operator_sum::operator*(const operator_sum \ operator_sum operator_sum::operator op( \ const operator_sum &other) const & { \ - operator_sum sum; \ - sum.tmap.reserve(this->tmap.size() + other.tmap.size()); \ - for (const auto &entry : this->tmap) \ - sum.tmap.insert(entry); \ - for (const auto &entry : other.tmap) \ - sum.insert(op entry.second); \ + operator_sum sum(*this, this->terms.size() + other.terms.size()); \ + for (auto i = 0; i < other.terms.size(); ++i) { \ + product_operator prod(op other.coefficients[i], other.terms[i]); \ + sum.insert(std::move(prod)); \ + } \ return sum; \ } \ + \ template \ operator_sum operator_sum::operator op( \ const operator_sum &other) && { \ @@ -619,13 +658,12 @@ INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(boson_operator); -#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - operator_sum &operator_sum::operator*=( \ - otherTy other) { \ - for (auto &entry : this->tmap) \ - entry.second.coefficient *= other; \ - return *this; \ +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum& operator_sum::operator*=(otherTy other) { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return *this; \ } SUM_MULTIPLICATION_ASSIGNMENT(double); @@ -649,14 +687,20 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template operator_sum& operator_sum::operator*=(const product_operator &other) { - std::vector> prods; - prods.reserve(this->tmap.size()); - for (auto it = this->tmap.begin(); it != this->tmap.end();) { - prods.push_back(std::move(it->second *= other)); - it = this->tmap.erase(it); + operator_sum sum; // the entire sum needs to be rebuilt + sum.coefficients.reserve(this->coefficients.size()); + sum.term_map.reserve(this->terms.size()); + sum.terms.reserve(this->terms.size()); + for (auto i = 0; i < this->terms.size(); ++i) { + product_operator prod(this->coefficients[i] * other.coefficient); + prod.operators.reserve(this->terms[i].size() + other.operators.size()); + for (const auto &op : this->terms[i]) + prod.operators.push_back(op); // no need to insert here - should be canonicalized already + for (HandlerTy op : other.operators) + prod.insert(std::move(op)); + sum.insert(std::move(prod)); } - for (auto &&prod : prods) - this->insert(std::move(prod)); + *this = std::move(sum); return *this; } @@ -672,30 +716,39 @@ SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template -operator_sum & -operator_sum::operator*=(const operator_sum &other) { - this->tmap.reserve(this->tmap.size() * other.tmap.size()); - std::vector> prods; - prods.reserve(this->tmap.size()); - for (auto it = this->tmap.cbegin(); it != this->tmap.cend();) { - prods.push_back(std::move(it->second)); - it = this->tmap.erase(it); - } - for (const auto &prod_self : prods) { - for (const auto &entry_other : other.tmap) - this->insert(prod_self * entry_other.second); +operator_sum& operator_sum::operator*=(const operator_sum &other) { + auto max_size = this->terms.size() * other.terms.size(); + operator_sum sum; // the entire sum needs to be rebuilt + sum.coefficients.reserve(max_size); + sum.term_map.reserve(max_size); + sum.terms.reserve(max_size); + for (auto i = 0; i < this->terms.size(); ++i) { + for (auto j = 0; j < other.terms.size(); ++j) { + product_operator prod(this->coefficients[i] * other.coefficients[j]); + prod.operators.reserve(this->terms[i].size() + other.terms[j].size()); + for (const auto &op : this->terms[i]) + prod.operators.push_back(op); // no need to insert here - should be canonicalized already + for (HandlerTy op : other.terms[j]) + prod.insert(std::move(op)); + sum.insert(std::move(prod)); + } } + *this = std::move(sum); return *this; } -#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - const operator_sum &other) { \ - this->tmap.reserve(this->tmap.size() + other.tmap.size()); \ - for (const auto &entry : other.tmap) \ - this->insert(op entry.second); \ - return *this; \ +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + template \ + operator_sum& operator_sum::operator op##=( \ + const operator_sum &other) { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator( \ + op other.coefficients[i], other.terms[i])); \ + return *this; \ } SUM_ADDITION_SUM_ASSIGNMENT(+); @@ -740,34 +793,28 @@ INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define SUM_MULTIPLICATION_REVERSE(otherTy) \ - template \ - operator_sum operator*(otherTy other, \ - const operator_sum &self) { \ - operator_sum sum; \ - sum.tmap.reserve(self.tmap.size()); \ - for (auto entry : self.tmap) { \ - entry.second.coefficient *= other; \ - sum.tmap.insert(entry); \ - } \ - return sum; \ +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum; \ + sum.coefficients.reserve(self.coefficients.size()); \ + sum.terms = self.terms; \ + sum.term_map = self.term_map; \ + for (const auto &coeff : self.coefficients) \ + sum.coefficients.push_back(coeff * other); \ + return sum; \ } SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); -#define SUM_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op(otherTy other, \ - const operator_sum &self) { \ - operator_sum sum; \ - sum.tmap.reserve(self.tmap.size() + 1); \ - for (auto entry : self.tmap) \ - sum.tmap.insert( \ - std::make_pair(entry.first, op std::move(entry.second))); \ - sum.insert(product_operator(other)); \ - return sum; \ +#define SUM_ADDITION_REVERSE(otherTy, op) \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + return std::move((op self) += other); \ } SUM_ADDITION_REVERSE(double, +); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 4492ac0256..7e2f86c308 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -43,78 +43,64 @@ bool product_operator::is_canonicalized() const { } #endif -template -std::vector::const_iterator -product_operator::find_insert_at(const HandlerTy &other) const { - // the logic below just ensures that terms are fully or partially ordered in - // canonical order - a best effort is made to order terms, but a full - // canonical ordering is not possible for certain handlers - return std::find_if( - this->operators.crbegin(), this->operators.crend(), - [&other_degrees = static_cast &>( - other.degrees())](const HandlerTy &self_op) { - const std::vector &self_op_degrees = self_op.degrees(); - for (auto other_degree : - other_degrees) { // fixme: special case on single qubit - // handlers instead? - auto item_it = std::find_if( - self_op_degrees.crbegin(), self_op_degrees.crend(), - [other_degree](int self_degree) { - return other_degree <= self_degree; - }); // FIXME: relies on canonical order - if (item_it != self_op_degrees.crend()) - return true; - } - return false; - }) - .base(); // base causes insert after for reverse iterator +template +std::vector::const_iterator product_operator::find_insert_at(const HandlerTy &other) const { + // the logic below just ensures that terms are fully or partially ordered in canonical order - + // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers + return std::find_if(this->operators.crbegin(), this->operators.crend(), + [other_target = other.target] (const HandlerTy& self_op) { + return other_target <= self_op.target; // FIXME: relies on canonical order + }).base(); // base causes insert after for reverse iterator } -template -template ::value && - !product_operator::supports_inplace_mult, - int>> -void product_operator::insert(T &&other, bool update_id) { +template<> +std::vector::const_iterator product_operator::find_insert_at(const matrix_operator &other) const { + // the logic below just ensures that terms are fully or partially ordered in canonical order - + // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers + return std::find_if(this->operators.crbegin(), this->operators.crend(), + [&other_degrees = static_cast&>(other.degrees())] + (const matrix_operator& self_op) { + const std::vector &self_op_degrees = self_op.degrees(); + for (auto other_degree : other_degrees) { + auto item_it = std::find_if(self_op_degrees.crbegin(), self_op_degrees.crend(), + [other_degree](int self_degree) { return other_degree <= self_degree; }); // FIXME: relies on canonical order + if (item_it != self_op_degrees.crend()) return true; + } + return false; + }).base(); // base causes insert after for reverse iterator +} + +template +template::value && !product_operator::supports_inplace_mult, int>> +void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); this->operators.insert(pos, other); - if (update_id) - this->update_id(); } -template -template ::value && - product_operator::supports_inplace_mult, - bool>> -void product_operator::insert(T &&other, bool update_id) { +template +template ::value && product_operator::supports_inplace_mult, bool>> +void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); - if (pos != this->operators.begin() && (pos - 1)->target == other.target) - this->coefficient *= - this->operators.erase(pos - 1, pos - 1) - ->inplace_mult( - other); // erase: constant time conversion to non-const iterator - else - this->operators.insert(pos, std::move(other)); - if (update_id) - this->update_id(); + if (pos != this->operators.begin() && (pos - 1)->target == other.target) + this->coefficient *= this->operators.erase(pos - 1, pos - 1)->inplace_mult(other); // erase: constant time conversion to non-const iterator + else this->operators.insert(pos, std::move(other)); } -template -void product_operator::update_id() { - this->term_id = ""; +template +std::string product_operator::get_term_id() const { + std::string term_id; for (const auto &op : this->operators) - this->term_id += op.unique_id(); + term_id += op.unique_id(); + return std::move(term_id); } template void product_operator::aggregate_terms() {} -template -template -void product_operator::aggregate_terms(HandlerTy &&head, - Args &&...args) { - this->insert(std::forward(head), false); +template +template +void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... args) { + this->insert(std::forward(head)); aggregate_terms(std::forward(args)...); } @@ -214,7 +200,7 @@ std::vector product_operator::degrees() const { auto degrees = std::vector(unsorted_degrees.cbegin(), unsorted_degrees.cend()); cudaq::detail::canonicalize_degrees(degrees); - return degrees; + return std::move(degrees); } template @@ -251,11 +237,11 @@ INSTANTIATE_PRODUCT_PROPERTIES(boson_operator); template product_operator::product_operator(double coefficient) - : coefficient(coefficient), term_id() {} + : coefficient(coefficient) {} template product_operator::product_operator(HandlerTy &&atomic) - : coefficient(1.), term_id(atomic.unique_id()) { + : coefficient(1.) { this->operators.push_back(std::move(atomic)); assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); // relevant for custom matrix operators @@ -271,92 +257,92 @@ product_operator::product_operator(scalar_operator coefficient, : coefficient(std::move(coefficient)) { this->operators.reserve(sizeof...(Args)); aggregate_terms(std::forward(args)...); - this->update_id(); - assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) -template -product_operator::product_operator( - scalar_operator coefficient, const std::vector &atomic_operators, - const std::string &term_id) - : coefficient(std::move(coefficient)), operators(atomic_operators), - term_id(term_id) { - assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); +template +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) + : coefficient(std::move(coefficient)), operators(atomic_operators) { + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) -template -product_operator::product_operator( - scalar_operator coefficient, std::vector &&atomic_operators, - std::string &&term_id) - : coefficient(std::move(coefficient)), - operators(std::move(atomic_operators)), term_id(std::move(term_id)) { - assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); +template +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) + : coefficient(std::move(coefficient)), operators(std::move(atomic_operators)) { + assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } -template -template ::value && - std::is_constructible::value, - bool>> -product_operator::product_operator(const product_operator &other) - : coefficient(other.coefficient) { +template +template::value && std::is_constructible::value, bool>> +product_operator::product_operator(const product_operator &other) + : coefficient(other.coefficient) { + this->operators.reserve(other.operators.size()); for (const T &other_op : other.operators) { HandlerTy op(other_op); this->operators.push_back(op); - this->term_id += op.unique_id(); } } -template -product_operator::product_operator( - const product_operator &other) - : coefficient(other.coefficient), term_id(other.term_id) { - this->operators = other.operators; +template +product_operator::product_operator(const product_operator &other, int size) + : coefficient(other.coefficient) { + if (size <= 0) this->operators = other.operators; + else { + this->operators.reserve(size); + for (const auto &op : other.operators) + this->operators.push_back(op); + } } -template -product_operator::product_operator( - product_operator &&other) - : coefficient(std::move(other.coefficient)), - term_id(std::move(other.term_id)) { - this->operators = std::move(other.operators); +template +product_operator::product_operator(product_operator &&other, int size) + : coefficient(std::move(other.coefficient)), operators(std::move(other.operators)) { + if (size > 0) this->operators.reserve(size); } -#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ - \ - template product_operator::product_operator(double coefficient); \ - \ - template product_operator::product_operator( \ - scalar_operator coefficient); \ - \ - template product_operator::product_operator(HandlerTy &&atomic); \ - \ - template product_operator::product_operator( \ - scalar_operator coefficient, HandlerTy &&atomic1); \ - \ - template product_operator::product_operator( \ - scalar_operator coefficient, HandlerTy &&atomic1, HandlerTy &&atomic2); \ - \ - template product_operator::product_operator( \ - scalar_operator coefficient, HandlerTy &&atomic1, HandlerTy &&atomic2, \ - HandlerTy &&atomic3); \ - \ - template product_operator::product_operator( \ - scalar_operator coefficient, \ - const std::vector &atomic_operators, \ - const std::string &term_id); \ - \ - template product_operator::product_operator( \ - scalar_operator coefficient, std::vector &&atomic_operators, \ - std::string &&term_id); \ - \ - template product_operator::product_operator( \ - const product_operator &other); \ - \ - template product_operator::product_operator( \ - product_operator &&other); +#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ + \ + template \ + product_operator::product_operator(double coefficient); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient); \ + \ + template \ + product_operator::product_operator(HandlerTy &&atomic); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + HandlerTy &&atomic1); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + HandlerTy &&atomic1, \ + HandlerTy &&atomic2); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + HandlerTy &&atomic1, \ + HandlerTy &&atomic2, \ + HandlerTy &&atomic3); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + const std::vector &atomic_operators); \ + \ + template \ + product_operator::product_operator(scalar_operator coefficient, \ + std::vector &&atomic_operators); \ + \ + template \ + product_operator::product_operator( \ + const product_operator &other, int size); \ + \ + template \ + product_operator::product_operator( \ + product_operator &&other, int size); template product_operator::product_operator( const product_operator &other); @@ -386,7 +372,6 @@ product_operator &product_operator::operator=( if (this != &other) { this->coefficient = other.coefficient; this->operators = other.operators; - this->term_id = other.term_id; } return *this; } @@ -397,7 +382,6 @@ product_operator::operator=(product_operator &&other) { if (this != &other) { this->coefficient = std::move(other.coefficient); this->operators = std::move(other.operators); - this->term_id = std::move(other.term_id); } return *this; } @@ -426,15 +410,16 @@ INSTANTIATE_PRODUCT_ASSIGNMENTS(boson_operator); template std::string product_operator::to_string() const { - return this->term_id; + auto str = "(" + this->coefficient.to_string() + ") * "; + for (const auto &op : this->operators) + str += op.to_string(true); + return std::move(str); } -template -matrix_2 product_operator::to_matrix( - std::unordered_map dimensions, - const std::unordered_map> ¶meters) - const { - return this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix(); +template +matrix_2 product_operator::to_matrix(std::unordered_map dimensions, + const std::unordered_map> ¶meters) const { + return std::move(this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix()); } #define INSTANTIATE_PRODUCT_EVALUATIONS(HandlerTy) \ @@ -452,11 +437,9 @@ INSTANTIATE_PRODUCT_EVALUATIONS(boson_operator); // comparisons -template -bool product_operator::operator==( - const product_operator &other) const { - return this->term_id == other.term_id && - this->coefficient == other.coefficient; +template +bool product_operator::operator==(const product_operator &other) const { + return this->coefficient == other.coefficient && this->get_term_id() == other.get_term_id(); } #define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ @@ -472,8 +455,7 @@ INSTANTIATE_PRODUCT_COMPARISONS(boson_operator); template product_operator product_operator::operator-() const & { - return product_operator(-1. * this->coefficient, this->operators, - this->term_id); + return product_operator(-1. * this->coefficient, this->operators); } template @@ -483,20 +465,24 @@ product_operator product_operator::operator-() && { } template -product_operator product_operator::operator+() const { +product_operator product_operator::operator+() const & { return *this; } -#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ - \ - template product_operator \ - product_operator::operator-() const &; \ - \ - template product_operator \ - product_operator::operator-() &&; \ - \ - template product_operator \ - product_operator::operator+() const; +template +product_operator product_operator::operator+() && { + return std::move(*this); +} + +#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ + template \ + product_operator product_operator::operator-() const &; \ + template \ + product_operator product_operator::operator-() &&; \ + template \ + product_operator product_operator::operator+() const &; \ + template \ + product_operator product_operator::operator+() &&; INSTANTIATE_PRODUCT_UNARY_OPS(matrix_operator); INSTANTIATE_PRODUCT_UNARY_OPS(spin_operator); @@ -504,12 +490,12 @@ INSTANTIATE_PRODUCT_UNARY_OPS(boson_operator); // right-hand arithmetics -#define PRODUCT_MULTIPLICATION(otherTy) \ - template \ - product_operator product_operator::operator*( \ - otherTy other) const { \ - return product_operator(other * this->coefficient, \ - this->operators, this->term_id); \ +#define PRODUCT_MULTIPLICATION(otherTy) \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const { \ + return product_operator(other * this->coefficient, \ + this->operators); \ } PRODUCT_MULTIPLICATION(double); @@ -557,48 +543,46 @@ INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(boson_operator); template -product_operator product_operator::operator*( - const product_operator &other) const { - std::vector terms; - terms.reserve(this->operators.size() + other.operators.size()); - for (auto &term : this->operators) - terms.push_back(term); - product_operator self(this->coefficient, std::move(terms), - this->term_id); - return self *= other; -} - -#define PRODUCT_ADDITION_PRODUCT(op) \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) const { \ - return operator_sum(op other, \ - product_operator(*this)); \ +product_operator product_operator::operator*(const product_operator &other) const { + product_operator prod(*this, this->operators.size() + other.operators.size()); + return std::move(prod *= other); +} + +#define PRODUCT_ADDITION_PRODUCT(op) \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const { \ + return operator_sum(product_operator(*this), op other); \ } PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) template -operator_sum product_operator::operator*( - const operator_sum &other) const { - operator_sum sum; - sum.tmap.reserve(other.tmap.size()); - for (const auto &entry : other.tmap) - sum.insert(*this * entry.second); +operator_sum product_operator::operator*(const operator_sum &other) const { + operator_sum sum; // everything needs to be updated, so creating a new sum makes sense + sum.coefficients.reserve(other.coefficients.size()); + sum.term_map.reserve(other.terms.size()); + sum.terms.reserve(other.terms.size()); + for (auto i = 0; i < other.terms.size(); ++i) { + auto prod = *this * product_operator(other.coefficients[i], other.terms[i]); + sum.insert(std::move(prod)); + } return sum; } -#define PRODUCT_ADDITION_SUM(op) \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) const { \ - operator_sum sum; \ - sum.tmap.reserve(other.tmap.size() + 1); \ - for (auto entry : other.tmap) /* copy here so pair doesn't copy */ \ - sum.tmap.insert(std::make_pair(entry.first, op entry.second)); \ - sum.insert(*this); \ - return sum; \ +#define PRODUCT_ADDITION_SUM(op) \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const { \ + operator_sum sum; \ + sum.coefficients.reserve(other.coefficients.size() + 1); \ + sum.term_map = other.term_map; \ + sum.terms = other.terms; \ + for (auto &coeff : other.coefficients) \ + sum.coefficients.push_back(op coeff); \ + sum.insert(*this); \ + return sum; \ } PRODUCT_ADDITION_SUM(+) @@ -641,8 +625,7 @@ product_operator &product_operator::operator*=( this->coefficient *= other.coefficient; this->operators.reserve(this->operators.size() + other.operators.size()); for (HandlerTy other_op : other.operators) - this->insert(std::move(other_op), false); - this->update_id(); + this->insert(std::move(other_op)); return *this; } @@ -664,12 +647,12 @@ INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ - template \ - product_operator operator*( \ - otherTy other, const product_operator &self) { \ - return product_operator(other * self.coefficient, \ - self.operators, self.term_id); \ +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + template \ + product_operator operator*(otherTy other, \ + const product_operator &self) { \ + return product_operator(other * self.coefficient, \ + self.operators); \ } PRODUCT_MULTIPLICATION_REVERSE(double); diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 01737665ed..28d6be6610 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -72,6 +72,14 @@ matrix_2 scalar_operator::to_matrix( return returnOperator; } +std::string scalar_operator::to_string() const { + if (std::holds_alternative>(this->value)) { + auto value = std::get>(this->value); + return "(" + std::to_string(value.real()) + "+" + std::to_string(value.imag()) + "i)"; + } + return "scalar"; +} + // comparison bool scalar_operator::operator==(scalar_operator other) const { diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 6736085c78..b498de3251 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -40,25 +40,25 @@ std::complex spin_operator::inplace_mult(const spin_operator &other) { else factor = -1.0j; this->op_code ^= other.op_code; - this->id = this->op_code_to_string() + std::to_string(target); return factor; } // read-only properties -const std::string &spin_operator::unique_id() const { return this->id; } +std::string spin_operator::unique_id() const { + return this->op_code_to_string() + std::to_string(target); +} std::vector spin_operator::degrees() const { return {this->target}; } // constructors -spin_operator::spin_operator(int target) - : op_code(0), target(target), id("I" + std::to_string(target)) {} +spin_operator::spin_operator(int target) + : op_code(0), target(target) {} -spin_operator::spin_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); - this->id = this->op_code_to_string() + std::to_string(target); +spin_operator::spin_operator(int target, int op_id) + : op_code(op_id), target(target) { + assert(0 <= op_id < 4); } // evaluations diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 8b4970a8c7..99bfeed893 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -21,14 +21,13 @@ template class product_operator; // FIXME: rename to spin ... -class spin_operator : public operator_handler { - friend class product_operator; +class spin_operator : public operator_handler{ +template friend class product_operator; private: // I = 0, Z = 1, X = 2, Y = 3 int op_code; int target; - std::string id; spin_operator(int target, int op_code); @@ -41,7 +40,7 @@ class spin_operator : public operator_handler { public: // read-only properties - virtual const std::string &unique_id() const; + virtual std::string unique_id() const; /// @brief The degrees of freedom that the operator acts on in canonical /// order. diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 6635030afa..3f4d555665 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -38,8 +38,6 @@ class operator_sum { void insert(product_operator &&other); void insert(const product_operator &other); - void aggregate_all(); - void aggregate_terms(); template @@ -49,7 +47,10 @@ class operator_sum { bool pad_terms = true) const; protected: - std::unordered_map> tmap; + + std::unordered_map term_map; // quick access to term index given its id (used for aggregating terms) + std::vector> terms; + std::vector coefficients; template = true> operator_sum(Args &&...args); - operator_sum(std::vector> &&terms); - public: // read-only properties @@ -83,10 +82,10 @@ class operator_sum { operator_sum(const operator_sum &other); // copy constructor - operator_sum(const operator_sum &other); + operator_sum(const operator_sum &other, int size = 0); // move constructor - operator_sum(operator_sum &&other); + operator_sum(operator_sum &&other, int size = 0); ~operator_sum() = default; @@ -271,19 +270,13 @@ class product_operator { typename std::vector::const_iterator find_insert_at(const HandlerTy &other) const; - template ::value && - !product_operator::supports_inplace_mult, - int> = 0> - void insert(T &&other, bool update_id); + template::value && !product_operator::supports_inplace_mult, int> = 0> + void insert(T &&other); - template ::value && - product_operator::supports_inplace_mult, - bool> = true> - void insert(T &&other, bool update_id); + template ::value && product_operator::supports_inplace_mult, bool> = true> + void insert(T &&other); - void update_id(); + std::string get_term_id() const; void aggregate_terms(); @@ -296,7 +289,6 @@ class product_operator { protected: std::vector operators; scalar_operator coefficient; - std::string term_id; template = true> product_operator(scalar_operator coefficient, Args &&...args); - // keep this constructor protected (otherwise it needs to ensure canonical - // order) - product_operator(scalar_operator coefficient, - const std::vector &atomic_operators, - const std::string &term_id); + // keep this constructor protected (otherwise it needs to ensure canonical order) + product_operator(scalar_operator coefficient, const std::vector &atomic_operators); - // keep this constructor protected (otherwise it needs to ensure canonical - // order) - product_operator(scalar_operator coefficient, - std::vector &&atomic_operators, - std::string &&term_id); + // keep this constructor protected (otherwise it needs to ensure canonical order) + product_operator(scalar_operator coefficient, std::vector &&atomic_operators); public: // read-only properties @@ -345,10 +331,10 @@ class product_operator { product_operator(const product_operator &other); // copy constructor - product_operator(const product_operator &other); + product_operator(const product_operator &other, int size = 0); // move constructor - product_operator(product_operator &&other); + product_operator(product_operator &&other, int size = 0); ~product_operator() = default; @@ -401,7 +387,8 @@ class product_operator { product_operator operator-() const &; product_operator operator-() &&; - product_operator operator+() const; + product_operator operator+() const &; + product_operator operator+() &&; // right-hand arithmetics diff --git a/scripts/build_cudaq.sh b/scripts/build_cudaq.sh index 95cbf387e0..eb559afaf3 100644 --- a/scripts/build_cudaq.sh +++ b/scripts/build_cudaq.sh @@ -72,8 +72,10 @@ this_file_dir=`dirname "$(readlink -f "${BASH_SOURCE[0]}")"` repo_root=$(cd "$this_file_dir" && git rev-parse --show-toplevel) # Prepare the build directory +build_dir="$working_dir/build/"$(echo "$build_configuration" | tr '[:upper:]' '[:lower:]') +echo "Build directory: $build_dir" mkdir -p "$CUDAQ_INSTALL_PREFIX/bin" -mkdir -p "$working_dir/build" && cd "$working_dir/build" # && rm -rf * +mkdir -p "$build_dir" && cd "$build_dir" # && rm -rf * mkdir -p logs && rm -rf logs/* if [ -n "$install_toolchain" ]; then diff --git a/v1.perf b/v1.perf index 88c5540841..1de34b75b3 100644 --- a/v1.perf +++ b/v1.perf @@ -1,106 +1,104 @@ multiplication inplace with itself nr ops: 100 -New setup took 0.000605938 seconds. +New setup took 2.8217e-05 seconds. nr ops: 1000 -New setup took 0.00326204 seconds. +New setup took 0.000122433 seconds. nr ops: 10000 -New setup took 0.0268849 seconds. +New setup took 0.0011764 seconds. multiplication inplace with other nr ops: 100 -New setup took 0.00259074 seconds. +New setup took 5.8016e-05 seconds. nr ops: 1000 -New setup took 0.178419 seconds. +New setup took 0.00074998 seconds. nr ops: 10000 -New setup took 17.7425 seconds. +New setup took 0.112577 seconds. multiplication inplace with other (reverse order) nr ops: 100 -New setup took 0.000488501 seconds. +New setup took 2.5015e-05 seconds. nr ops: 1000 -New setup took 0.0257551 seconds. +New setup took 0.000481467 seconds. nr ops: 10000 -New setup took 2.63377 seconds. +New setup took 0.0743091 seconds. addition inplace with itself nr ops: 100 -New setup took 0.000423601 seconds. +New setup took 3.7362e-05 seconds. nr ops: 1000 -New setup took 0.00248007 seconds. +New setup took 0.000141091 seconds. nr ops: 10000 -New setup took 0.0197457 seconds. +New setup took 0.00151542 seconds. addition inplace with other nr ops: 100 -New setup took 0.000704163 seconds. +New setup took 4.2818e-05 seconds. nr ops: 1000 -New setup took 0.0130536 seconds. +New setup took 0.000304375 seconds. nr ops: 10000 -New setup took 1.08428 seconds. +New setup took 0.00325976 seconds. addition inplace with other (reverse order) nr ops: 100 -New setup took 0.000478452 seconds. +New setup took 3.1124e-05 seconds. nr ops: 1000 -New setup took 0.0126688 seconds. +New setup took 0.000248507 seconds. nr ops: 10000 -New setup took 1.03774 seconds. +New setup took 0.00285919 seconds. addition inplace with product self nr ops: 100 -New setup took 0.000741696 seconds. +New setup took 0.00024566 seconds. nr ops: 1000 -New setup took 0.00571034 seconds. +New setup took 0.00236887 seconds. nr ops: 10000 -New setup took 0.0663077 seconds. +New setup took 0.0230741 seconds. addition inplace with product self (reverse order) nr ops: 100 -New setup took 0.000913494 seconds. +New setup took 0.000246139 seconds. nr ops: 1000 -New setup took 0.0076885 seconds. +New setup took 0.00232941 seconds. nr ops: 10000 -New setup took 0.0662493 seconds. +New setup took 0.0231446 seconds. product inplace with 2-term sum on fixed degrees nr ops: 100 -New setup took 0.0125319 seconds. +New setup took 0.000190887 seconds. nr ops: 1000 -New setup took 0.0631849 seconds. +New setup took 0.00181602 seconds. nr ops: 10000 -New setup took 0.740012 seconds. +New setup took 0.0201958 seconds. product inplace with 2-term sum on varying degrees nr ops: 10 -New setup took 0.0349756 seconds. +New setup took 0.00175238 seconds. nr ops: 20 -New setup took 65.7295 seconds. -nr ops: 30 +New setup took 3.00176 seconds. product inplace with 2-term sum on varying degrees (reverse order) nr ops: 10 -New setup took 0.028038 seconds. +New setup took 0.0012573 seconds. nr ops: 20 -New setup took 56.2411 seconds. -nr ops: 30 +New setup took 2.47935 seconds. sum of products of random terms nr terms 10, term length 100 -New setup took 0.00712887 seconds. +New setup took 0.000131233 seconds. nr terms 10, term length 1000 -New setup took 0.146541 seconds. +New setup took 0.000725484 seconds. nr terms 10, term length 10000 -New setup took 1.54605 seconds. +New setup took 0.00554068 seconds. nr terms 100, term length 100 -New setup took 0.0734078 seconds. +New setup took 0.00098111 seconds. nr terms 100, term length 1000 -New setup took 1.41369 seconds. +New setup took 0.00613552 seconds. nr terms 100, term length 10000 -New setup took 15.3141 seconds. +New setup took 0.0517298 seconds. nr terms 1000, term length 100 -New setup took 0.849971 seconds. +New setup took 0.0115695 seconds. nr terms 1000, term length 1000 -New setup took 14.1757 seconds. +New setup took 0.062983 seconds. nr terms 1000, term length 10000 -New setup took 152.821 seconds. +New setup took 0.503548 seconds. diff --git a/v3.perf b/v3.perf new file mode 100644 index 0000000000..8a5086fb58 --- /dev/null +++ b/v3.perf @@ -0,0 +1,104 @@ + +multiplication inplace with itself +nr ops: 100 +New setup took 3.2052e-05 seconds. +nr ops: 1000 +New setup took 0.000199063 seconds. +nr ops: 10000 +New setup took 0.00133742 seconds. + +multiplication inplace with other +nr ops: 100 +New setup took 5.0597e-05 seconds. +nr ops: 1000 +New setup took 0.000686551 seconds. +nr ops: 10000 +New setup took 0.11252 seconds. + +multiplication inplace with other (reverse order) +nr ops: 100 +New setup took 2.1386e-05 seconds. +nr ops: 1000 +New setup took 0.000379773 seconds. +nr ops: 10000 +New setup took 0.0739069 seconds. + +addition inplace with itself +nr ops: 100 +New setup took 3.3719e-05 seconds. +nr ops: 1000 +New setup took 0.000165188 seconds. +nr ops: 10000 +New setup took 0.00190972 seconds. + +addition inplace with other +nr ops: 100 +New setup took 5.0923e-05 seconds. +nr ops: 1000 +New setup took 0.00035224 seconds. +nr ops: 10000 +New setup took 0.00349925 seconds. + +addition inplace with other (reverse order) +nr ops: 100 +New setup took 4.9642e-05 seconds. +nr ops: 1000 +New setup took 0.000276181 seconds. +nr ops: 10000 +New setup took 0.00371862 seconds. + +addition inplace with product self +nr ops: 100 +New setup took 0.000232237 seconds. +nr ops: 1000 +New setup took 0.00230737 seconds. +nr ops: 10000 +New setup took 0.0234588 seconds. + +addition inplace with product self (reverse order) +nr ops: 100 +New setup took 0.000242613 seconds. +nr ops: 1000 +New setup took 0.00237575 seconds. +nr ops: 10000 +New setup took 0.0243036 seconds. + +product inplace with 2-term sum on fixed degrees +nr ops: 100 +New setup took 0.00013055 seconds. +nr ops: 1000 +New setup took 0.00166417 seconds. +nr ops: 10000 +New setup took 0.0176478 seconds. + +product inplace with 2-term sum on varying degrees +nr ops: 10 +New setup took 0.000959758 seconds. +nr ops: 20 +New setup took 2.74868 seconds. + +product inplace with 2-term sum on varying degrees (reverse order) +nr ops: 10 +New setup took 0.00090642 seconds. +nr ops: 20 +New setup took 2.46003 seconds. + +sum of products of random terms +nr terms 10, term length 100 +New setup took 0.000126657 seconds. +nr terms 10, term length 1000 +New setup took 0.000673385 seconds. +nr terms 10, term length 10000 +New setup took 0.0049152 seconds. +nr terms 100, term length 100 +New setup took 0.000991959 seconds. +nr terms 100, term length 1000 +New setup took 0.00621974 seconds. +nr terms 100, term length 10000 +New setup took 0.0498803 seconds. +nr terms 1000, term length 100 +New setup took 0.0122335 seconds. +nr terms 1000, term length 1000 +New setup took 0.0625441 seconds. +nr terms 1000, term length 10000 +New setup took 0.497919 seconds. From a3ad658cfb6a0940cb5abcb13728cdb4194c270e Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Mon, 17 Feb 2025 23:44:53 +0000 Subject: [PATCH 284/311] adding missing rvalue overloads Signed-off-by: Bettina Heim --- .devcontainer/devcontainer.json | 4 +- runtime/cudaq/dynamics/operator_sum.cpp | 359 ++++++++++------ runtime/cudaq/dynamics/product_operators.cpp | 406 ++++++++++++++----- runtime/cudaq/dynamics/templates.h | 259 +++++++----- runtime/cudaq/operators.h | 336 +++++++++------ v3.perf | 80 ++-- 6 files changed, 957 insertions(+), 487 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 9fb984055d..8536275b04 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -241,9 +241,9 @@ "C_Cpp.inlayHints.referenceOperator.enabled": false, "C_Cpp.doxygen.generateOnType": false, "C_Cpp.default.cStandard": "c17", - "C_Cpp.default.cppStandard": "c++20", + "C_Cpp.default.cppStandard": "c++17", "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools", - "C_Cpp.default.compileCommands": "${workspaceFolder}/build/compile_commands.json", + "C_Cpp.default.compileCommands": "${workspaceFolder}/build/debug/compile_commands.json", "C_Cpp.default.includePath": [ "${workspaceFolder}/**", "${CUDAQ_INSTALL_PREFIX}/**", diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 00c3c70d24..19dd745abd 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -399,7 +399,7 @@ operator_sum operator_sum::operator-() const & { sum.terms = this->terms; for (auto &coeff : this->coefficients) sum.coefficients.push_back(-1. * coeff); - return sum; + return std::move(sum); } template @@ -449,12 +449,14 @@ INSTANTIATE_SUM_UNARY_OPS(boson_operator); sum.terms = this->terms; \ for (const auto &coeff : this->coefficients) \ sum.coefficients.push_back(coeff * other); \ - return sum; \ + return std::move(sum); \ } \ \ template \ operator_sum operator_sum::operator*(otherTy other) && { \ - return std::move(*this *= other); \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return std::move(*this); \ } SUM_MULTIPLICATION(double); @@ -472,7 +474,8 @@ SUM_MULTIPLICATION(const scalar_operator &); \ template \ operator_sum operator_sum::operator op(otherTy other) && { \ - return std::move(*this op##= other); \ + this->insert(product_operator(op other)); \ + return std::move(*this); \ } \ SUM_ADDITION(double, +); @@ -526,16 +529,14 @@ INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template -operator_sum operator_sum::operator*(const product_operator &other) const & { - operator_sum sum; +operator_sum operator_sum::operator*(const product_operator &other) const { + operator_sum sum; // the entire sum needs to be rebuilt sum.coefficients.reserve(this->coefficients.size()); sum.term_map.reserve(this->terms.size()); sum.terms.reserve(this->terms.size()); for (auto i = 0; i < this->terms.size(); ++i) { - product_operator prod(this->coefficients[i] * other.coefficient); - prod.operators.reserve(this->terms[i].size() + other.operators.size()); - for (const auto &op : this->terms[i]) - prod.operators.push_back(op); // no need to insert here - should be canonicalized already + auto max_size = this->terms[i].size() + other.operators.size(); + product_operator prod(this->coefficients[i] * other.coefficient, this->terms[i], max_size); for (HandlerTy op : other.operators) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -543,11 +544,6 @@ operator_sum operator_sum::operator*(const product_operato return std::move(sum); } -template -operator_sum operator_sum::operator*(const product_operator &other) && { - return std::move(*this *= other); -} - #define SUM_ADDITION_PRODUCT(op) \ \ template \ @@ -561,36 +557,45 @@ operator_sum operator_sum::operator*(const product_operato template \ operator_sum operator_sum::operator op( \ const product_operator &other) && { \ - return std::move(*this op##= other); \ - } + this->insert(op other); \ + return std::move(*this); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + product_operator &&other) const & { \ + operator_sum sum(*this, this->terms.size() + 1); \ + sum.insert(op std::move(other)); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + product_operator &&other) && { \ + this->insert(op std::move(other)); \ + return std::move(*this); \ + } \ SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum operator_sum::operator*(const operator_sum &other) const & { - operator_sum sum; +operator_sum operator_sum::operator*(const operator_sum &other) const { + operator_sum sum; // the entire sum needs to be rebuilt auto max_size = this->terms.size() * other.terms.size(); sum.coefficients.reserve(max_size); sum.term_map.reserve(max_size); sum.terms.reserve(max_size); for (auto i = 0; i < this->terms.size(); ++i) { for (auto j = 0; j < other.terms.size(); ++j) { - product_operator prod(this->coefficients[i] * other.coefficients[j]); - prod.operators.reserve(this->terms[i].size() + other.terms[j].size()); - for (const auto &op : this->terms[i]) - prod.operators.push_back(op); // no need to insert here - should be canonicalized already + auto max_size = this->terms[i].size() + other.terms[j].size(); + product_operator prod(this->coefficients[i] * other.coefficients[j], this->terms[i], max_size); for (HandlerTy op : other.terms[j]) prod.insert(std::move(op)); sum.insert(std::move(prod)); } } - return sum; -} - -template -operator_sum operator_sum::operator*(const operator_sum &other) && { - return std::move(*this *= other); + return std::move(sum); } #define SUM_ADDITION_SUM(op) \ @@ -603,13 +608,45 @@ operator_sum operator_sum::operator*(const operator_sum prod(op other.coefficients[i], other.terms[i]); \ sum.insert(std::move(prod)); \ } \ - return sum; \ + return std::move(sum); \ } \ \ template \ operator_sum operator_sum::operator op( \ const operator_sum &other) && { \ - return std::move(*this op##= other); \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator( \ + op other.coefficients[i], other.terms[i])); \ + return std::move(*this); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + operator_sum &&other) const & { \ + operator_sum sum(*this, this->terms.size() + other.terms.size()); \ + for (auto i = 0; i < other.terms.size(); ++i) { \ + product_operator prod( \ + op std::move(other.coefficients[i]), std::move(other.terms[i])); \ + sum.insert(std::move(prod)); \ + } \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + operator_sum &&other) && { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator( \ + op std::move(other.coefficients[i]), std::move(other.terms[i]))); \ + return std::move(*this); \ } \ SUM_ADDITION_SUM(+); @@ -619,16 +656,19 @@ SUM_ADDITION_SUM(-); \ template \ operator_sum operator_sum::operator*( \ + const product_operator &other) const; \ + template \ + operator_sum operator_sum::operator+( \ const product_operator &other) const &; \ template \ - operator_sum operator_sum::operator*( \ + operator_sum operator_sum::operator+( \ const product_operator &other) &&; \ template \ operator_sum operator_sum::operator+( \ - const product_operator &other) const &; \ + product_operator &&other) const &; \ template \ operator_sum operator_sum::operator+( \ - const product_operator &other) &&; \ + product_operator &&other) &&; \ template \ operator_sum operator_sum::operator-( \ const product_operator &other) const &; \ @@ -636,11 +676,14 @@ SUM_ADDITION_SUM(-); operator_sum operator_sum::operator-( \ const product_operator &other) &&; \ template \ - operator_sum operator_sum::operator*( \ - const operator_sum &other) const &; \ + operator_sum operator_sum::operator-( \ + product_operator &&other) const &; \ + template \ + operator_sum operator_sum::operator-( \ + product_operator &&other) &&; \ template \ operator_sum operator_sum::operator*( \ - const operator_sum &other) &&; \ + const operator_sum &other) const; \ template \ operator_sum operator_sum::operator+( \ const operator_sum &other) const &; \ @@ -648,11 +691,23 @@ SUM_ADDITION_SUM(-); operator_sum operator_sum::operator+( \ const operator_sum &other) &&; \ template \ + operator_sum operator_sum::operator+( \ + operator_sum &&other) const &; \ + template \ + operator_sum operator_sum::operator+( \ + operator_sum &&other) &&; \ + template \ operator_sum operator_sum::operator-( \ const operator_sum &other) const &; \ template \ operator_sum operator_sum::operator-( \ - const operator_sum &other) &&; + const operator_sum &other) &&; \ + template \ + operator_sum operator_sum::operator-( \ + operator_sum &&other) const &; \ + template \ + operator_sum operator_sum::operator-( \ + operator_sum &&other) &&; \ INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); @@ -664,19 +719,18 @@ INSTANTIATE_SUM_RHCOMPOSITE_OPS(boson_operator); for (auto &coeff : this->coefficients) \ coeff *= other; \ return *this; \ - } + } \ SUM_MULTIPLICATION_ASSIGNMENT(double); SUM_MULTIPLICATION_ASSIGNMENT(std::complex); SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - otherTy other) { \ - this->insert(product_operator(op other)); \ - return *this; \ - } +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum& operator_sum::operator op##=(otherTy other) { \ + this->insert(product_operator(op other)); \ + return *this; \ + } \ SUM_ADDITION_ASSIGNMENT(double, +); SUM_ADDITION_ASSIGNMENT(double, -); @@ -687,15 +741,13 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template operator_sum& operator_sum::operator*=(const product_operator &other) { - operator_sum sum; // the entire sum needs to be rebuilt + operator_sum sum; sum.coefficients.reserve(this->coefficients.size()); sum.term_map.reserve(this->terms.size()); sum.terms.reserve(this->terms.size()); for (auto i = 0; i < this->terms.size(); ++i) { - product_operator prod(this->coefficients[i] * other.coefficient); - prod.operators.reserve(this->terms[i].size() + other.operators.size()); - for (const auto &op : this->terms[i]) - prod.operators.push_back(op); // no need to insert here - should be canonicalized already + auto max_size = this->terms[i].size() + other.operators.size(); + product_operator prod(this->coefficients[i] * other.coefficient, this->terms[i], max_size); for (HandlerTy op : other.operators) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -704,30 +756,36 @@ operator_sum& operator_sum::operator*=(const product_opera return *this; } -#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ - template \ - operator_sum &operator_sum::operator op##=( \ - const product_operator &other) { \ - this->insert(op other); \ - return *this; \ - } +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + \ + template \ + operator_sum& operator_sum::operator op##=( \ + const product_operator &other) { \ + this->insert(op other); \ + return *this; \ + } \ + \ + template \ + operator_sum& operator_sum::operator op##=( \ + product_operator &&other) { \ + this->insert(op std::move(other)); \ + return *this; \ + } \ SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template operator_sum& operator_sum::operator*=(const operator_sum &other) { - auto max_size = this->terms.size() * other.terms.size(); operator_sum sum; // the entire sum needs to be rebuilt + auto max_size = this->terms.size() * other.terms.size(); sum.coefficients.reserve(max_size); sum.term_map.reserve(max_size); sum.terms.reserve(max_size); for (auto i = 0; i < this->terms.size(); ++i) { for (auto j = 0; j < other.terms.size(); ++j) { - product_operator prod(this->coefficients[i] * other.coefficients[j]); - prod.operators.reserve(this->terms[i].size() + other.terms[j].size()); - for (const auto &op : this->terms[i]) - prod.operators.push_back(op); // no need to insert here - should be canonicalized already + auto max_size = this->terms[i].size() + other.terms[j].size(); + product_operator prod(this->coefficients[i] * other.coefficients[j], this->terms[i], max_size); for (HandlerTy op : other.terms[j]) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -738,6 +796,7 @@ operator_sum& operator_sum::operator*=(const operator_sum< } #define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + \ template \ operator_sum& operator_sum::operator op##=( \ const operator_sum &other) { \ @@ -749,43 +808,64 @@ operator_sum& operator_sum::operator*=(const operator_sum< this->insert(product_operator( \ op other.coefficients[i], other.terms[i])); \ return *this; \ - } + } \ + \ + template \ + operator_sum& operator_sum::operator op##=( \ + operator_sum &&other) { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator( \ + op std::move(other.coefficients[i]), std::move(other.terms[i]))); \ + return *this; \ + } \ SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); -#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ - \ - template operator_sum &operator_sum::operator*=( \ - double other); \ - template operator_sum &operator_sum::operator+=( \ - double other); \ - template operator_sum &operator_sum::operator-=( \ - double other); \ - template operator_sum &operator_sum::operator*=( \ - std::complex other); \ - template operator_sum &operator_sum::operator+=( \ - std::complex other); \ - template operator_sum &operator_sum::operator-=( \ - std::complex other); \ - template operator_sum &operator_sum::operator*=( \ - const scalar_operator &other); \ - template operator_sum &operator_sum::operator+=( \ - const scalar_operator &other); \ - template operator_sum &operator_sum::operator-=( \ - const scalar_operator &other); \ - template operator_sum &operator_sum::operator*=( \ - const product_operator &other); \ - template operator_sum &operator_sum::operator+=( \ - const product_operator &other); \ - template operator_sum &operator_sum::operator-=( \ - const product_operator &other); \ - template operator_sum &operator_sum::operator*=( \ - const operator_sum &other); \ - template operator_sum &operator_sum::operator-=( \ - const operator_sum &other); \ - template operator_sum &operator_sum::operator+=( \ - const operator_sum &other); +#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ + \ + template \ + operator_sum& operator_sum::operator*=(double other); \ + template \ + operator_sum& operator_sum::operator+=(double other); \ + template \ + operator_sum& operator_sum::operator-=(double other); \ + template \ + operator_sum& operator_sum::operator*=(std::complex other); \ + template \ + operator_sum& operator_sum::operator+=(std::complex other); \ + template \ + operator_sum& operator_sum::operator-=(std::complex other); \ + template \ + operator_sum& operator_sum::operator*=(const scalar_operator &other); \ + template \ + operator_sum& operator_sum::operator+=(const scalar_operator &other); \ + template \ + operator_sum& operator_sum::operator-=(const scalar_operator &other); \ + template \ + operator_sum& operator_sum::operator*=(const product_operator &other); \ + template \ + operator_sum& operator_sum::operator+=(const product_operator &other); \ + template \ + operator_sum& operator_sum::operator+=(product_operator &&other); \ + template \ + operator_sum& operator_sum::operator-=(const product_operator &other); \ + template \ + operator_sum& operator_sum::operator-=(product_operator &&other); \ + template \ + operator_sum& operator_sum::operator*=(const operator_sum &other); \ + template \ + operator_sum& operator_sum::operator+=(const operator_sum &other); \ + template \ + operator_sum& operator_sum::operator+=(operator_sum &&other); \ + template \ + operator_sum& operator_sum::operator-=(const operator_sum &other); \ + template \ + operator_sum& operator_sum::operator-=(operator_sum &&other); \ INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); @@ -794,6 +874,7 @@ INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); // left-hand arithmetics #define SUM_MULTIPLICATION_REVERSE(otherTy) \ + \ template \ operator_sum operator*(otherTy other, \ const operator_sum &self) { \ @@ -803,19 +884,39 @@ INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); sum.term_map = self.term_map; \ for (const auto &coeff : self.coefficients) \ sum.coefficients.push_back(coeff * other); \ - return sum; \ - } + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator*(otherTy other, \ + operator_sum &&self) { \ + for (auto &&coeff : self.coefficients) \ + coeff *= other; \ + return std::move(self); \ + } \ SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); #define SUM_ADDITION_REVERSE(otherTy, op) \ + \ template \ operator_sum operator op(otherTy other, \ const operator_sum &self) { \ - return std::move((op self) += other); \ - } + operator_sum sum(op self); \ + sum.insert(product_operator(other)); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator op(otherTy other, \ + operator_sum &&self) { \ + for (auto &&coeff : self.coefficients) \ + coeff = std::move(op coeff); \ + self.insert(product_operator(other)); \ + return std::move(self); \ + } \ SUM_ADDITION_REVERSE(double, +); SUM_ADDITION_REVERSE(double, -); @@ -824,26 +925,44 @@ SUM_ADDITION_REVERSE(std::complex, -); SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); -#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ - \ - template operator_sum operator*( \ - double other, const operator_sum &self); \ - template operator_sum operator+( \ - double other, const operator_sum &self); \ - template operator_sum operator-( \ - double other, const operator_sum &self); \ - template operator_sum operator*( \ - std::complex other, const operator_sum &self); \ - template operator_sum operator+( \ - std::complex other, const operator_sum &self); \ - template operator_sum operator-( \ - std::complex other, const operator_sum &self); \ - template operator_sum operator*( \ - const scalar_operator &other, const operator_sum &self); \ - template operator_sum operator+( \ - const scalar_operator &other, const operator_sum &self); \ - template operator_sum operator-( \ - const scalar_operator &other, const operator_sum &self); +#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + operator_sum operator*(double other, const operator_sum &self); \ + template \ + operator_sum operator*(double other, operator_sum &&self); \ + template \ + operator_sum operator+(double other, const operator_sum &self); \ + template \ + operator_sum operator+(double other, operator_sum &&self); \ + template \ + operator_sum operator-(double other, const operator_sum &self); \ + template \ + operator_sum operator-(double other, operator_sum &&self); \ + template \ + operator_sum operator*(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator*(std::complex other, operator_sum &&self); \ + template \ + operator_sum operator+(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator+(std::complex other, operator_sum &&self); \ + template \ + operator_sum operator-(std::complex other, const operator_sum &self); \ + template \ + operator_sum operator-(std::complex other, operator_sum &&self); \ + template \ + operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator*(const scalar_operator &other, operator_sum &&self); \ + template \ + operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator+(const scalar_operator &other, operator_sum &&self); \ + template \ + operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ + template \ + operator_sum operator-(const scalar_operator &other, operator_sum &&self); \ INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 7e2f86c308..97d4113413 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -262,15 +262,22 @@ product_operator::product_operator(scalar_operator coefficient, // assumes canonical ordering (if possible) template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators) - : coefficient(std::move(coefficient)), operators(atomic_operators) { +product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators, int size) + : coefficient(std::move(coefficient)) { + if (size <= 0) this->operators = atomic_operators; + else { + this->operators.reserve(size); + for (const auto &op : atomic_operators) + this->operators.push_back(op); + } assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators) +product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators, int size) : coefficient(std::move(coefficient)), operators(std::move(atomic_operators)) { + if (size > 0) this->operators.reserve(size); assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } @@ -330,11 +337,11 @@ product_operator::product_operator(product_operator &&othe \ template \ product_operator::product_operator(scalar_operator coefficient, \ - const std::vector &atomic_operators); \ + const std::vector &atomic_operators, int size); \ \ template \ product_operator::product_operator(scalar_operator coefficient, \ - std::vector &&atomic_operators); \ + std::vector &&atomic_operators, int size); \ \ template \ product_operator::product_operator( \ @@ -461,7 +468,7 @@ product_operator product_operator::operator-() const & { template product_operator product_operator::operator-() && { this->coefficient *= -1.; - return *this; + return std::move(*this); } template @@ -491,24 +498,40 @@ INSTANTIATE_PRODUCT_UNARY_OPS(boson_operator); // right-hand arithmetics #define PRODUCT_MULTIPLICATION(otherTy) \ + \ template \ product_operator product_operator::operator*( \ - otherTy other) const { \ + otherTy other) const & { \ return product_operator(other * this->coefficient, \ this->operators); \ - } + } \ + \ + template \ + product_operator product_operator::operator*( \ + otherTy other) && { \ + this->coefficient *= other; \ + return std::move(*this); \ + } \ PRODUCT_MULTIPLICATION(double); PRODUCT_MULTIPLICATION(std::complex); PRODUCT_MULTIPLICATION(const scalar_operator &); -#define PRODUCT_ADDITION(otherTy, op) \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) const { \ - return operator_sum(product_operator(op other), \ - product_operator(*this)); \ - } +#define PRODUCT_ADDITION(otherTy, op) \ + \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const & { \ + return operator_sum(product_operator(op other), \ + product_operator(*this)); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) && { \ + return operator_sum(product_operator(op other), \ + std::move(*this)); \ + } \ PRODUCT_ADDITION(double, +); PRODUCT_ADDITION(double, -); @@ -517,43 +540,111 @@ PRODUCT_ADDITION(std::complex, -); PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); -#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ - \ - template product_operator product_operator::operator*( \ - double other) const; \ - template operator_sum product_operator::operator+( \ - double other) const; \ - template operator_sum product_operator::operator-( \ - double other) const; \ - template product_operator product_operator::operator*( \ - std::complex other) const; \ - template operator_sum product_operator::operator+( \ - std::complex other) const; \ - template operator_sum product_operator::operator-( \ - std::complex other) const; \ - template product_operator product_operator::operator*( \ - const scalar_operator &other) const; \ - template operator_sum product_operator::operator+( \ - const scalar_operator &other) const; \ - template operator_sum product_operator::operator-( \ - const scalar_operator &other) const; +#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ + \ + template \ + product_operator product_operator::operator*(double other) const &; \ + template \ + product_operator product_operator::operator*(double other) &&; \ + template \ + operator_sum product_operator::operator+(double other) const &; \ + template \ + operator_sum product_operator::operator+(double other) &&; \ + template \ + operator_sum product_operator::operator-(double other) const &; \ + template \ + operator_sum product_operator::operator-(double other) &&; \ + template \ + product_operator product_operator::operator*(std::complex other) const &; \ + template \ + product_operator product_operator::operator*(std::complex other) &&; \ + template \ + operator_sum product_operator::operator+(std::complex other) const &; \ + template \ + operator_sum product_operator::operator+(std::complex other) &&; \ + template \ + operator_sum product_operator::operator-(std::complex other) const &; \ + template \ + operator_sum product_operator::operator-(std::complex other) &&; \ + template \ + product_operator product_operator::operator*(const scalar_operator &other) const &; \ + template \ + product_operator product_operator::operator*(const scalar_operator &other) &&; \ + template \ + operator_sum product_operator::operator+(const scalar_operator &other) const &; \ + template \ + operator_sum product_operator::operator+(const scalar_operator &other) &&; \ + template \ + operator_sum product_operator::operator-(const scalar_operator &other) const &; \ + template \ + operator_sum product_operator::operator-(const scalar_operator &other) &&; \ INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(boson_operator); template -product_operator product_operator::operator*(const product_operator &other) const { - product_operator prod(*this, this->operators.size() + other.operators.size()); - return std::move(prod *= other); +product_operator product_operator::operator*(const product_operator &other) const & { + product_operator prod(this->coefficient * other.coefficient, this->operators, + this->operators.size() + other.operators.size()); + for (HandlerTy op : other.operators) + prod.insert(std::move(op)); + return std::move(prod); +} + +template +product_operator product_operator::operator*(const product_operator &other) && { + this->coefficient *= other.coefficient; + this->operators.reserve(this->operators.size() + other.operators.size()); + for (HandlerTy op : other.operators) + this->insert(std::move(op)); + return std::move(*this); +} + +template +product_operator product_operator::operator*(product_operator &&other) const & { + product_operator prod(this->coefficient * std::move(other.coefficient), + this->operators, this->operators.size() + other.operators.size()); + for (auto &&op : other.operators) + prod.insert(std::move(op)); + return std::move(prod); +} + +template +product_operator product_operator::operator*(product_operator &&other) && { + this->coefficient *= std::move(other.coefficient); + this->operators.reserve(this->operators.size() + other.operators.size()); + for (auto &&op : other.operators) + this->insert(std::move(op)); + return std::move(*this); } #define PRODUCT_ADDITION_PRODUCT(op) \ + \ template \ operator_sum product_operator::operator op( \ - const product_operator &other) const { \ + const product_operator &other) const & { \ return operator_sum(product_operator(*this), op other); \ - } + } \ + \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) && { \ + return operator_sum(std::move(*this), op other); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + product_operator &&other) const & { \ + return operator_sum(product_operator(*this), \ + op std::move(other)); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + product_operator &&other) && { \ + return operator_sum(std::move(*this), op std::move(other)); \ + } \ PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) @@ -568,13 +659,14 @@ operator_sum product_operator::operator*(const operator_su auto prod = *this * product_operator(other.coefficients[i], other.terms[i]); sum.insert(std::move(prod)); } - return sum; + return std::move(sum); } #define PRODUCT_ADDITION_SUM(op) \ + \ template \ operator_sum product_operator::operator op( \ - const operator_sum &other) const { \ + const operator_sum &other) const & { \ operator_sum sum; \ sum.coefficients.reserve(other.coefficients.size() + 1); \ sum.term_map = other.term_map; \ @@ -582,26 +674,99 @@ operator_sum product_operator::operator*(const operator_su for (auto &coeff : other.coefficients) \ sum.coefficients.push_back(op coeff); \ sum.insert(*this); \ - return sum; \ - } + return std::move(sum); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) && { \ + operator_sum sum; \ + sum.coefficients.reserve(other.coefficients.size() + 1); \ + sum.term_map = other.term_map; \ + sum.terms = other.terms; \ + for (auto &coeff : other.coefficients) \ + sum.coefficients.push_back(op coeff); \ + sum.insert(std::move(*this)); \ + return std::move(sum); \ + } \ + template \ + operator_sum product_operator::operator op( \ + operator_sum &&other) const & { \ + operator_sum sum(op std::move(other)); \ + sum.insert(*this); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + operator_sum &&other) && { \ + operator_sum sum(op std::move(other)); \ + sum.insert(std::move(*this)); \ + return std::move(sum); \ + } \ PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) -#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ - \ - template product_operator product_operator::operator*( \ - const product_operator &other) const; \ - template operator_sum product_operator::operator+( \ - const product_operator &other) const; \ - template operator_sum product_operator::operator-( \ - const product_operator &other) const; \ - template operator_sum product_operator::operator*( \ - const operator_sum &other) const; \ - template operator_sum product_operator::operator+( \ - const operator_sum &other) const; \ - template operator_sum product_operator::operator-( \ - const operator_sum &other) const; +#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + product_operator product_operator::operator*( \ + const product_operator &other) const &; \ + template \ + product_operator product_operator::operator*( \ + const product_operator &other) &&; \ + template \ + operator_sum product_operator::operator+( \ + const product_operator &other) const &; \ + template \ + operator_sum product_operator::operator+( \ + const product_operator &other) &&; \ + template \ + operator_sum product_operator::operator+( \ + product_operator &&other) const &; \ + template \ + operator_sum product_operator::operator+( \ + product_operator &&other) &&; \ + template \ + operator_sum product_operator::operator-( \ + const product_operator &other) const &; \ + template \ + operator_sum product_operator::operator-( \ + const product_operator &other) &&; \ + template \ + operator_sum product_operator::operator-( \ + product_operator &&other) const &; \ + template \ + operator_sum product_operator::operator-( \ + product_operator &&other) &&; \ + template \ + operator_sum product_operator::operator*( \ + const operator_sum &other) const; \ + template \ + operator_sum product_operator::operator+( \ + const operator_sum &other) const &; \ + template \ + operator_sum product_operator::operator+( \ + const operator_sum &other) &&; \ + template \ + operator_sum product_operator::operator+( \ + operator_sum &&other) const &; \ + template \ + operator_sum product_operator::operator+( \ + operator_sum &&other) &&; \ + template \ + operator_sum product_operator::operator-( \ + const operator_sum &other) const &; \ + template \ + operator_sum product_operator::operator-( \ + const operator_sum &other) &&; \ + template \ + operator_sum product_operator::operator-( \ + operator_sum &&other) const &; \ + template \ + operator_sum product_operator::operator-( \ + operator_sum &&other) &&; \ INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(spin_operator); @@ -624,22 +789,32 @@ product_operator &product_operator::operator*=( const product_operator &other) { this->coefficient *= other.coefficient; this->operators.reserve(this->operators.size() + other.operators.size()); - for (HandlerTy other_op : other.operators) - this->insert(std::move(other_op)); + for (HandlerTy op : other.operators) + this->insert(std::move(op)); return *this; } -#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ - \ - template product_operator & \ - product_operator::operator*=(double other); \ - template product_operator & \ - product_operator::operator*=(std::complex other); \ - template product_operator & \ - product_operator::operator*=(const scalar_operator &other); \ - template product_operator & \ - product_operator::operator*=( \ - const product_operator &other); +template +product_operator& product_operator::operator*=(product_operator &&other) { + this->coefficient *= std::move(other.coefficient); + this->operators.reserve(this->operators.size() + other.operators.size()); + for (auto &&op : other.operators) + this->insert(std::move(op)); + return *this; +} + +#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ + \ + template \ + product_operator& product_operator::operator*=(double other); \ + template \ + product_operator& product_operator::operator*=(std::complex other); \ + template \ + product_operator& product_operator::operator*=(const scalar_operator &other); \ + template \ + product_operator& product_operator::operator*=(const product_operator &other); \ + template \ + product_operator& product_operator::operator*=(product_operator &&other); \ INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); @@ -648,24 +823,39 @@ INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); // left-hand arithmetics #define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + \ template \ product_operator operator*(otherTy other, \ const product_operator &self) { \ return product_operator(other * self.coefficient, \ self.operators); \ - } + } \ + \ + template \ + product_operator operator*(otherTy other, \ + product_operator &&self) { \ + self.coefficient *= other; \ + return std::move(self); \ + } \ PRODUCT_MULTIPLICATION_REVERSE(double); PRODUCT_MULTIPLICATION_REVERSE(std::complex); PRODUCT_MULTIPLICATION_REVERSE(const scalar_operator &); -#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ - template \ - operator_sum operator op( \ - otherTy other, const product_operator &self) { \ - return operator_sum(product_operator(other), \ - op self); \ - } +#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ + \ + template \ + operator_sum operator op(otherTy other, \ + const product_operator &self) { \ + return operator_sum(product_operator(other), op self); \ + } \ + \ + template \ + operator_sum operator op(otherTy other, \ + product_operator &&self) { \ + return operator_sum(product_operator(other), \ + op std::move(self)); \ + } \ PRODUCT_ADDITION_REVERSE(double, +); PRODUCT_ADDITION_REVERSE(double, -); @@ -674,26 +864,44 @@ PRODUCT_ADDITION_REVERSE(std::complex, -); PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); -#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ - \ - template product_operator operator*( \ - double other, const product_operator &self); \ - template operator_sum operator+( \ - double other, const product_operator &self); \ - template operator_sum operator-( \ - double other, const product_operator &self); \ - template product_operator operator*( \ - std::complex other, const product_operator &self); \ - template operator_sum operator+( \ - std::complex other, const product_operator &self); \ - template operator_sum operator-( \ - std::complex other, const product_operator &self); \ - template product_operator operator*( \ - const scalar_operator &other, const product_operator &self); \ - template operator_sum operator+( \ - const scalar_operator &other, const product_operator &self); \ - template operator_sum operator-( \ - const scalar_operator &other, const product_operator &self); +#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template \ + product_operator operator*(double other, const product_operator &self); \ + template \ + product_operator operator*(double other, product_operator &&self); \ + template \ + operator_sum operator+(double other, const product_operator &self); \ + template \ + operator_sum operator+(double other, product_operator &&self); \ + template \ + operator_sum operator-(double other, const product_operator &self); \ + template \ + operator_sum operator-(double other, product_operator &&self); \ + template \ + product_operator operator*(std::complex other, const product_operator &self); \ + template \ + product_operator operator*(std::complex other, product_operator &&self); \ + template \ + operator_sum operator+(std::complex other, const product_operator &self); \ + template \ + operator_sum operator+(std::complex other, product_operator &&self); \ + template \ + operator_sum operator-(std::complex other, const product_operator &self); \ + template \ + operator_sum operator-(std::complex other, product_operator &&self); \ + template \ + product_operator operator*(const scalar_operator &other, const product_operator &self); \ + template \ + product_operator operator*(const scalar_operator &other, product_operator &&self); \ + template \ + operator_sum operator+(const scalar_operator &other, const product_operator &self); \ + template \ + operator_sum operator+(const scalar_operator &other, product_operator &&self); \ + template \ + operator_sum operator-(const scalar_operator &other, const product_operator &self); \ + template \ + operator_sum operator-(const scalar_operator &other, product_operator &&self); \ INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 41ad71de45..632b6693f4 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -31,33 +31,42 @@ class operator_sum; std::is_base_of::value, \ bool> -template -product_operator operator*(double other, - const product_operator &self); -template -operator_sum operator+(double other, - const product_operator &self); -template -operator_sum operator-(double other, - const product_operator &self); -template -product_operator operator*(std::complex other, - const product_operator &self); -template -operator_sum operator+(std::complex other, - const product_operator &self); -template -operator_sum operator-(std::complex other, - const product_operator &self); -template -product_operator operator*(const scalar_operator &other, - const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, - const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, - const product_operator &self); +template +product_operator operator*(double other, const product_operator &self); +template +product_operator operator*(double other, product_operator &&self); +template +operator_sum operator+(double other, const product_operator &self); +template +operator_sum operator+(double other, product_operator &&self); +template +operator_sum operator-(double other, const product_operator &self); +template +operator_sum operator-(double other, product_operator &&self); +template +product_operator operator*(std::complex other, const product_operator &self); +template +product_operator operator*(std::complex other, product_operator &&self); +template +operator_sum operator+(std::complex other, const product_operator &self); +template +operator_sum operator+(std::complex other, product_operator &&self); +template +operator_sum operator-(std::complex other, const product_operator &self); +template +operator_sum operator-(std::complex other, product_operator &&self); +template +product_operator operator*(const scalar_operator &other, const product_operator &self); +template +product_operator operator*(const scalar_operator &other, product_operator &&self); +template +operator_sum operator+(const scalar_operator &other, const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, product_operator &&self); +template +operator_sum operator-(const scalar_operator &other, const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, product_operator &&self); template @@ -73,33 +82,61 @@ template operator-(const product_operator &other, const product_operator &self); -template -operator_sum operator*(double other, - const operator_sum &self); -template -operator_sum operator+(double other, - const operator_sum &self); -template -operator_sum operator-(double other, - const operator_sum &self); -template -operator_sum operator*(std::complex other, - const operator_sum &self); -template -operator_sum operator+(std::complex other, - const operator_sum &self); -template -operator_sum operator-(std::complex other, - const operator_sum &self); -template -operator_sum operator*(const scalar_operator &other, - const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, - const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, - const operator_sum &self); +template +operator_sum operator*(double other, const operator_sum &self); +template +operator_sum operator*(double other, operator_sum &&self); +template +operator_sum operator+(double other, const operator_sum &self); +template +operator_sum operator+(double other, operator_sum &&self); +template +operator_sum operator-(double other, const operator_sum &self); +template +operator_sum operator-(double other, operator_sum &&self); +template +operator_sum operator*(std::complex other, const operator_sum &self); +template +operator_sum operator*(std::complex other, operator_sum &&self); +template +operator_sum operator+(std::complex other, const operator_sum &self); +template +operator_sum operator+(std::complex other, operator_sum &&self); +template +operator_sum operator-(std::complex other, const operator_sum &self); +template +operator_sum operator-(std::complex other, operator_sum &&self); +template +operator_sum operator*(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator*(const scalar_operator &other, operator_sum &&self); +template +operator_sum operator+(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, operator_sum &&self); +template +operator_sum operator-(const scalar_operator &other, const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, operator_sum &&self); + +template +operator_sum operator*(const operator_sum &other, const product_operator &self); +template +operator_sum operator+(const operator_sum &other, const product_operator &self); +template +operator_sum operator-(const operator_sum &other, const product_operator &self); +template +operator_sum operator*(const product_operator &other, const operator_sum &self); +template +operator_sum operator+(const product_operator &other, const operator_sum &self); +template +operator_sum operator-(const product_operator &other, const operator_sum &self); +template +operator_sum operator*(const operator_sum &other, const operator_sum &self); +template +operator_sum operator+(const operator_sum &other, const operator_sum &self); +template +operator_sum operator-(const operator_sum &other, const operator_sum &self); template @@ -139,45 +176,81 @@ operator_sum operator-(const operator_sum &other, const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES -#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ - \ - extern template product_operator operator*( \ - double other, const product_operator &self); \ - extern template operator_sum operator+( \ - double other, const product_operator &self); \ - extern template operator_sum operator-( \ - double other, const product_operator &self); \ - extern template product_operator operator*( \ - std::complex other, const product_operator &self); \ - extern template operator_sum operator+( \ - std::complex other, const product_operator &self); \ - extern template operator_sum operator-( \ - std::complex other, const product_operator &self); \ - extern template product_operator operator*( \ - const scalar_operator &other, const product_operator &self); \ - extern template operator_sum operator+( \ - const scalar_operator &other, const product_operator &self); \ - extern template operator_sum operator-( \ - const scalar_operator &other, const product_operator &self); \ - \ - extern template operator_sum operator*( \ - double other, const operator_sum &self); \ - extern template operator_sum operator+( \ - double other, const operator_sum &self); \ - extern template operator_sum operator-( \ - double other, const operator_sum &self); \ - extern template operator_sum operator*( \ - std::complex other, const operator_sum &self); \ - extern template operator_sum operator+( \ - std::complex other, const operator_sum &self); \ - extern template operator_sum operator-( \ - std::complex other, const operator_sum &self); \ - extern template operator_sum operator*( \ - const scalar_operator &other, const operator_sum &self); \ - extern template operator_sum operator+( \ - const scalar_operator &other, const operator_sum &self); \ - extern template operator_sum operator-( \ - const scalar_operator &other, const operator_sum &self); +#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ + \ + extern template \ + product_operator operator*(double other, const product_operator &self); \ + extern template \ + product_operator operator*(double other, product_operator &&self); \ + extern template \ + operator_sum operator+(double other, const product_operator &self); \ + extern template \ + operator_sum operator+(double other, product_operator &&self); \ + extern template \ + operator_sum operator-(double other, const product_operator &self); \ + extern template \ + operator_sum operator-(double other, product_operator &&self); \ + extern template \ + product_operator operator*(std::complex other, const product_operator &self); \ + extern template \ + product_operator operator*(std::complex other, product_operator &&self); \ + extern template \ + operator_sum operator+(std::complex other, const product_operator &self); \ + extern template \ + operator_sum operator+(std::complex other, product_operator &&self); \ + extern template \ + operator_sum operator-(std::complex other, const product_operator &self); \ + extern template \ + operator_sum operator-(std::complex other, product_operator &&self); \ + extern template \ + product_operator operator*(const scalar_operator &other, const product_operator &self); \ + extern template \ + product_operator operator*(const scalar_operator &other, product_operator &&self); \ + extern template \ + operator_sum operator+(const scalar_operator &other, const product_operator &self); \ + extern template \ + operator_sum operator+(const scalar_operator &other, product_operator &&self); \ + extern template \ + operator_sum operator-(const scalar_operator &other, const product_operator &self); \ + extern template \ + operator_sum operator-(const scalar_operator &other, product_operator &&self); \ + \ + extern template \ + operator_sum operator*(double other, const operator_sum &self); \ + extern template \ + operator_sum operator*(double other, operator_sum &&self); \ + extern template \ + operator_sum operator+(double other, const operator_sum &self); \ + extern template \ + operator_sum operator+(double other, operator_sum &&self); \ + extern template \ + operator_sum operator-(double other, const operator_sum &self); \ + extern template \ + operator_sum operator-(double other, operator_sum &&self); \ + extern template \ + operator_sum operator*(std::complex other, const operator_sum &self); \ + extern template \ + operator_sum operator*(std::complex other, operator_sum &&self); \ + extern template \ + operator_sum operator+(std::complex other, const operator_sum &self); \ + extern template \ + operator_sum operator+(std::complex other, operator_sum &&self); \ + extern template \ + operator_sum operator-(std::complex other, const operator_sum &self); \ + extern template \ + operator_sum operator-(std::complex other, operator_sum &&self); \ + extern template \ + operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ + extern template \ + operator_sum operator*(const scalar_operator &other, operator_sum &&self); \ + extern template \ + operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ + extern template \ + operator_sum operator+(const scalar_operator &other, operator_sum &&self); \ + extern template \ + operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ + extern template \ + operator_sum operator-(const scalar_operator &other, operator_sum &&self); \ EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 3f4d555665..b6b5e8bd51 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -156,34 +156,44 @@ class operator_sum { operator_sum operator+(const scalar_operator &other) &&; operator_sum operator-(const scalar_operator &other) const &; operator_sum operator-(const scalar_operator &other) &&; - operator_sum operator*(const product_operator &other) const &; - operator_sum operator*(const product_operator &other) &&; + operator_sum operator*(const product_operator &other) const; operator_sum operator+(const product_operator &other) const &; operator_sum operator+(const product_operator &other) &&; + operator_sum operator+(product_operator &&other) const &; + operator_sum operator+(product_operator &&other) &&; operator_sum operator-(const product_operator &other) const &; operator_sum operator-(const product_operator &other) &&; - operator_sum operator*(const operator_sum &other) const &; - operator_sum operator*(const operator_sum &other) &&; + operator_sum operator-(product_operator &&other) const &; + operator_sum operator-(product_operator &&other) &&; + operator_sum operator*(const operator_sum &other) const; operator_sum operator+(const operator_sum &other) const &; operator_sum operator+(const operator_sum &other) &&; + operator_sum operator+(operator_sum &&other) const &; + operator_sum operator+(operator_sum &&other) &&; operator_sum operator-(const operator_sum &other) const &; operator_sum operator-(const operator_sum &other) &&; - - operator_sum &operator*=(double other); - operator_sum &operator+=(double other); - operator_sum &operator-=(double other); - operator_sum &operator*=(std::complex other); - operator_sum &operator+=(std::complex other); - operator_sum &operator-=(std::complex other); - operator_sum &operator*=(const scalar_operator &other); - operator_sum &operator+=(const scalar_operator &other); - operator_sum &operator-=(const scalar_operator &other); - operator_sum &operator*=(const product_operator &other); - operator_sum &operator+=(const product_operator &other); - operator_sum &operator-=(const product_operator &other); - operator_sum &operator*=(const operator_sum &other); - operator_sum &operator+=(const operator_sum &other); - operator_sum &operator-=(const operator_sum &other); + operator_sum operator-(operator_sum &&other) const &; + operator_sum operator-(operator_sum &&other) &&; + + operator_sum& operator*=(double other); + operator_sum& operator+=(double other); + operator_sum& operator-=(double other); + operator_sum& operator*=(std::complex other); + operator_sum& operator+=(std::complex other); + operator_sum& operator-=(std::complex other); + operator_sum& operator*=(const scalar_operator &other); + operator_sum& operator+=(const scalar_operator &other); + operator_sum& operator-=(const scalar_operator &other); + operator_sum& operator*=(const product_operator &other); + operator_sum& operator+=(const product_operator &other); + operator_sum& operator+=(product_operator &&other); + operator_sum& operator-=(const product_operator &other); + operator_sum& operator-=(product_operator &&other); + operator_sum& operator*=(const operator_sum &other); + operator_sum& operator+=(const operator_sum &other); + operator_sum& operator+=(operator_sum &&other); + operator_sum& operator-=(const operator_sum &other); + operator_sum& operator-=(operator_sum &&other); // left-hand arithmetics @@ -191,47 +201,65 @@ class operator_sum { // instantiation is a nightmare. template friend operator_sum operator*(double other, const operator_sum &self); - template + template + friend operator_sum operator*(double other, operator_sum &&self); + template friend operator_sum operator+(double other, const operator_sum &self); - template + template + friend operator_sum operator+(double other, operator_sum &&self); + template friend operator_sum operator-(double other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, - const operator_sum &self); - template - friend operator_sum operator+(std::complex other, - const operator_sum &self); - template - friend operator_sum operator-(std::complex other, - const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, - const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, - const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, - const operator_sum &self); - - template - friend operator_sum operator+(double other, - const product_operator &self); - template - friend operator_sum operator-(double other, - const product_operator &self); - template - friend operator_sum operator+(std::complex other, - const product_operator &self); - template - friend operator_sum operator-(std::complex other, - const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, - const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, - const product_operator &self); + template + friend operator_sum operator-(double other, operator_sum &&self); + template + friend operator_sum operator*(std::complex other, const operator_sum &self); + template + friend operator_sum operator*(std::complex other, operator_sum &&self); + template + friend operator_sum operator+(std::complex other, const operator_sum &self); + template + friend operator_sum operator+(std::complex other, operator_sum &&self); + template + friend operator_sum operator-(std::complex other, const operator_sum &self); + template + friend operator_sum operator-(std::complex other, operator_sum &&self); + template + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, operator_sum &&self); + template + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, operator_sum &&self); + template + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, operator_sum &&self); + + template + friend operator_sum operator+(double other, const product_operator &self); + template + friend operator_sum operator+(double other, product_operator &&self); + template + friend operator_sum operator-(double other, const product_operator &self); + template + friend operator_sum operator-(double other, product_operator &&self); + template + friend operator_sum operator+(std::complex other, const product_operator &self); + template + friend operator_sum operator+(std::complex other, product_operator &&self); + template + friend operator_sum operator-(std::complex other, const product_operator &self); + template + friend operator_sum operator-(std::complex other, product_operator &&self); + template + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, product_operator &&self); + template + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, product_operator &&self); // common operators @@ -297,10 +325,10 @@ class product_operator { product_operator(scalar_operator coefficient, Args &&...args); // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, const std::vector &atomic_operators); + product_operator(scalar_operator coefficient, const std::vector &atomic_operators, int size = 0); // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, std::vector &&atomic_operators); + product_operator(scalar_operator coefficient, std::vector &&atomic_operators, int size = 0); public: // read-only properties @@ -392,86 +420,128 @@ class product_operator { // right-hand arithmetics - product_operator operator*(double other) const; - operator_sum operator+(double other) const; - operator_sum operator-(double other) const; - product_operator operator*(std::complex other) const; - operator_sum operator+(std::complex other) const; - operator_sum operator-(std::complex other) const; - product_operator operator*(const scalar_operator &other) const; - operator_sum operator+(const scalar_operator &other) const; - operator_sum operator-(const scalar_operator &other) const; - product_operator - operator*(const product_operator &other) const; - operator_sum - operator+(const product_operator &other) const; - operator_sum - operator-(const product_operator &other) const; + product_operator operator*(double other) const &; + product_operator operator*(double other) &&; + operator_sum operator+(double other) const &; + operator_sum operator+(double other) &&; + operator_sum operator-(double other) const &; + operator_sum operator-(double other) &&; + product_operator operator*(std::complex other) const &; + product_operator operator*(std::complex other) &&; + operator_sum operator+(std::complex other) const &; + operator_sum operator+(std::complex other) &&; + operator_sum operator-(std::complex other) const &; + operator_sum operator-(std::complex other) &&; + product_operator operator*(const scalar_operator &other) const &; + product_operator operator*(const scalar_operator &other) &&; + operator_sum operator+(const scalar_operator &other) const &; + operator_sum operator+(const scalar_operator &other) &&; + operator_sum operator-(const scalar_operator &other) const &; + operator_sum operator-(const scalar_operator &other) &&; + product_operator operator*(const product_operator &other) const &; + product_operator operator*(const product_operator &other) &&; + product_operator operator*(product_operator &&other) const &; + product_operator operator*(product_operator &&other) &&; + operator_sum operator+(const product_operator &other) const &; + operator_sum operator+(const product_operator &other) &&; + operator_sum operator+(product_operator &&other) const &; + operator_sum operator+(product_operator &&other) &&; + operator_sum operator-(const product_operator &other) const &; + operator_sum operator-(const product_operator &other) &&; + operator_sum operator-(product_operator &&other) const &; + operator_sum operator-(product_operator &&other) &&; operator_sum operator*(const operator_sum &other) const; - operator_sum operator+(const operator_sum &other) const; - operator_sum operator-(const operator_sum &other) const; + operator_sum operator+(const operator_sum &other) const &; + operator_sum operator+(const operator_sum &other) &&; + operator_sum operator+(operator_sum &&other) const &; + operator_sum operator+(operator_sum &&other) &&; + operator_sum operator-(const operator_sum &other) const &; + operator_sum operator-(const operator_sum &other) &&; + operator_sum operator-(operator_sum &&other) const &; + operator_sum operator-(operator_sum &&other) &&; - product_operator &operator*=(double other); - product_operator &operator*=(std::complex other); - product_operator &operator*=(const scalar_operator &other); - product_operator & - operator*=(const product_operator &other); + product_operator& operator*=(double other); + product_operator& operator*=(std::complex other); + product_operator& operator*=(const scalar_operator &other); + product_operator& operator*=(const product_operator &other); + product_operator& operator*=(product_operator &&other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template - // instantiation is a nightmare. - template - friend product_operator operator*(double other, - const product_operator &self); - template - friend operator_sum operator+(double other, - const product_operator &self); - template - friend operator_sum operator-(double other, - const product_operator &self); - template + // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. + template + friend product_operator operator*(double other, const product_operator &self); + template + friend product_operator operator*(double other, product_operator &&self); + template + friend operator_sum operator+(double other, const product_operator &self); + template + friend operator_sum operator+(double other, product_operator &&self); + template + friend operator_sum operator-(double other, const product_operator &self); + template + friend operator_sum operator-(double other, product_operator &&self); + template + friend product_operator operator*(std::complex other, const product_operator &self); + template + friend product_operator operator*(std::complex other, product_operator &&self); + template + friend operator_sum operator+(std::complex other, const product_operator &self); + template + friend operator_sum operator+(std::complex other, product_operator &&self); + template + friend operator_sum operator-(std::complex other, const product_operator &self); + template + friend operator_sum operator-(std::complex other, product_operator &&self); + template + friend product_operator operator*(const scalar_operator &other, const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, product_operator &&self); + template + friend operator_sum operator+(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, product_operator &&self); + template + friend operator_sum operator-(const scalar_operator &other, const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, product_operator &&self); + + template friend operator_sum operator*(double other, const operator_sum &self); - template + template + friend operator_sum operator*(double other, operator_sum &&self); + template friend operator_sum operator+(double other, const operator_sum &self); - template + template + friend operator_sum operator+(double other, operator_sum &&self); + template friend operator_sum operator-(double other, const operator_sum &self); - template - friend product_operator operator*(std::complex other, - const product_operator &self); - template - friend operator_sum operator+(std::complex other, - const product_operator &self); - template - friend operator_sum operator-(std::complex other, - const product_operator &self); - template - friend operator_sum operator*(std::complex other, - const operator_sum &self); - template - friend operator_sum operator+(std::complex other, - const operator_sum &self); - template - friend operator_sum operator-(std::complex other, - const operator_sum &self); - template - friend product_operator operator*(const scalar_operator &other, - const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, - const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, - const product_operator &self); - template - friend operator_sum operator*(const scalar_operator &other, - const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, - const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, - const operator_sum &self); + template + friend operator_sum operator-(double other, operator_sum &&self); + template + friend operator_sum operator*(std::complex other, const operator_sum &self); + template + friend operator_sum operator*(std::complex other, operator_sum &&self); + template + friend operator_sum operator+(std::complex other, const operator_sum &self); + template + friend operator_sum operator+(std::complex other, operator_sum &&self); + template + friend operator_sum operator-(std::complex other, const operator_sum &self); + template + friend operator_sum operator-(std::complex other, operator_sum &&self); + template + friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, operator_sum &&self); + template + friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, operator_sum &&self); + template + friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, operator_sum &&self); // common operators diff --git a/v3.perf b/v3.perf index 8a5086fb58..27bba8ec26 100644 --- a/v3.perf +++ b/v3.perf @@ -1,104 +1,104 @@ multiplication inplace with itself nr ops: 100 -New setup took 3.2052e-05 seconds. +New setup took 2.6647e-05 seconds. nr ops: 1000 -New setup took 0.000199063 seconds. +New setup took 0.000107774 seconds. nr ops: 10000 -New setup took 0.00133742 seconds. +New setup took 0.00142158 seconds. multiplication inplace with other nr ops: 100 -New setup took 5.0597e-05 seconds. +New setup took 4.9103e-05 seconds. nr ops: 1000 -New setup took 0.000686551 seconds. +New setup took 0.000718737 seconds. nr ops: 10000 -New setup took 0.11252 seconds. +New setup took 0.122428 seconds. multiplication inplace with other (reverse order) nr ops: 100 -New setup took 2.1386e-05 seconds. +New setup took 2.1195e-05 seconds. nr ops: 1000 -New setup took 0.000379773 seconds. +New setup took 0.000388353 seconds. nr ops: 10000 -New setup took 0.0739069 seconds. +New setup took 0.0703014 seconds. addition inplace with itself nr ops: 100 -New setup took 3.3719e-05 seconds. +New setup took 3.8241e-05 seconds. nr ops: 1000 -New setup took 0.000165188 seconds. +New setup took 0.000111467 seconds. nr ops: 10000 -New setup took 0.00190972 seconds. +New setup took 0.00124563 seconds. addition inplace with other nr ops: 100 -New setup took 5.0923e-05 seconds. +New setup took 4.8936e-05 seconds. nr ops: 1000 -New setup took 0.00035224 seconds. +New setup took 0.000299443 seconds. nr ops: 10000 -New setup took 0.00349925 seconds. +New setup took 0.00407947 seconds. addition inplace with other (reverse order) nr ops: 100 -New setup took 4.9642e-05 seconds. +New setup took 7.1686e-05 seconds. nr ops: 1000 -New setup took 0.000276181 seconds. +New setup took 0.000210669 seconds. nr ops: 10000 -New setup took 0.00371862 seconds. +New setup took 0.00241548 seconds. addition inplace with product self nr ops: 100 -New setup took 0.000232237 seconds. +New setup took 0.000253843 seconds. nr ops: 1000 -New setup took 0.00230737 seconds. +New setup took 0.00227643 seconds. nr ops: 10000 -New setup took 0.0234588 seconds. +New setup took 0.0242446 seconds. addition inplace with product self (reverse order) nr ops: 100 -New setup took 0.000242613 seconds. +New setup took 0.00023314 seconds. nr ops: 1000 -New setup took 0.00237575 seconds. +New setup took 0.00234443 seconds. nr ops: 10000 -New setup took 0.0243036 seconds. +New setup took 0.0233708 seconds. product inplace with 2-term sum on fixed degrees nr ops: 100 -New setup took 0.00013055 seconds. +New setup took 0.000148147 seconds. nr ops: 1000 -New setup took 0.00166417 seconds. +New setup took 0.00119577 seconds. nr ops: 10000 -New setup took 0.0176478 seconds. +New setup took 0.0119991 seconds. product inplace with 2-term sum on varying degrees nr ops: 10 -New setup took 0.000959758 seconds. +New setup took 0.000662715 seconds. nr ops: 20 -New setup took 2.74868 seconds. +New setup took 2.84599 seconds. product inplace with 2-term sum on varying degrees (reverse order) nr ops: 10 -New setup took 0.00090642 seconds. +New setup took 0.00101627 seconds. nr ops: 20 -New setup took 2.46003 seconds. +New setup took 2.26694 seconds. sum of products of random terms nr terms 10, term length 100 -New setup took 0.000126657 seconds. +New setup took 0.000133817 seconds. nr terms 10, term length 1000 -New setup took 0.000673385 seconds. +New setup took 0.000672323 seconds. nr terms 10, term length 10000 -New setup took 0.0049152 seconds. +New setup took 0.00515006 seconds. nr terms 100, term length 100 -New setup took 0.000991959 seconds. +New setup took 0.00111038 seconds. nr terms 100, term length 1000 -New setup took 0.00621974 seconds. +New setup took 0.0058762 seconds. nr terms 100, term length 10000 -New setup took 0.0498803 seconds. +New setup took 0.0498336 seconds. nr terms 1000, term length 100 -New setup took 0.0122335 seconds. +New setup took 0.00966492 seconds. nr terms 1000, term length 1000 -New setup took 0.0625441 seconds. +New setup took 0.0642393 seconds. nr terms 1000, term length 10000 -New setup took 0.497919 seconds. +New setup took 0.494563 seconds. From 8ee54f22154805d352221442cf59033de6f337be Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 18 Feb 2025 09:09:59 -0800 Subject: [PATCH 285/311] Fixing Scalar callback using Python callback from cudensitymat and formatting Signed-off-by: Sachin Pisal --- main.cpp | 421 ++++---- runtime/cudaq/dynamics/boson_operators.cpp | 9 +- runtime/cudaq/dynamics/boson_operators.h | 10 +- runtime/cudaq/dynamics/matrix_operators.cpp | 33 +- runtime/cudaq/dynamics/operator_sum.cpp | 986 +++++++++--------- runtime/cudaq/dynamics/product_operators.cpp | 845 +++++++-------- runtime/cudaq/dynamics/scalar_operators.cpp | 3 +- runtime/cudaq/dynamics/spin_operators.cpp | 9 +- runtime/cudaq/dynamics/spin_operators.h | 5 +- runtime/cudaq/dynamics/templates.h | 375 ++++--- runtime/cudaq/operators.h | 434 ++++---- runtime/cudaq/schedule.h | 2 +- .../nvqir/cudensitymat/CuDensityMatState.cpp | 5 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 38 +- unittests/dynamics/test_cudm_expectation.cpp | 8 +- unittests/dynamics/test_cudm_helpers.cpp | 5 +- unittests/dynamics/test_cudm_state.cpp | 7 +- unittests/dynamics/test_cudm_time_stepper.cpp | 23 +- .../dynamics/test_runge_kutta_integrator.cpp | 9 +- 19 files changed, 1722 insertions(+), 1505 deletions(-) diff --git a/main.cpp b/main.cpp index b444c33d4a..4c43968376 100644 --- a/main.cpp +++ b/main.cpp @@ -24,28 +24,28 @@ int main() { for (auto nr_reps : repetitions) { - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = - cudaq::spin_operator::identity(); + cudaq::spin_op spin_op = cudaq::spin_op(); + cudaq::product_operator prod_op = + cudaq::spin_operator::identity(); std::cout << "nr ops: " << nr_reps << std::endl; - + if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } @@ -55,55 +55,58 @@ int main() { for (auto nr_reps : repetitions) { cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = cudaq::spin_operator::identity(); + cudaq::product_operator prod_op = + cudaq::spin_operator::identity(); std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } // multiplication inplace with other (reverse order) - std::cout << std::endl << "multiplication inplace with other (reverse order)" << std::endl; + std::cout << std::endl + << "multiplication inplace with other (reverse order)" << std::endl; for (auto nr_reps : repetitions) { cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = cudaq::spin_operator::identity(); + cudaq::product_operator prod_op = + cudaq::spin_operator::identity(); std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= cudaq::spin::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + prod_op *= cudaq::spin_operator::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } @@ -118,21 +121,21 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += cudaq::spin::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(0); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } @@ -147,29 +150,31 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - if (nr_reps > 1000) std::cout << "Old setup takes minutes - skipped" << std::endl; - else { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - } - - if (run_new) { + if (nr_reps > 1000) + std::cout << "Old setup takes minutes - skipped" << std::endl; + else { auto start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(i); + spin_op += cudaq::spin::x(i); auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } // addition inplace with other (reverse order) - std::cout << std::endl << "addition inplace with other (reverse order)" << std::endl; + std::cout << std::endl + << "addition inplace with other (reverse order)" << std::endl; for (auto nr_reps : repetitions) { @@ -179,24 +184,25 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - if (nr_reps > 1000) std::cout << "Old setup takes minutes - skipped" << std::endl; - else { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - } - - if (run_new) { + if (nr_reps > 1000) + std::cout << "Old setup takes minutes - skipped" << std::endl; + else { auto start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(nr_reps - i); + spin_op += cudaq::spin::x(nr_reps - i); auto stop = std::chrono::high_resolution_clock::now(); auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } + } + + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += cudaq::spin_operator::x(nr_reps - i); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } @@ -207,71 +213,74 @@ int main() { auto spin_prod = cudaq::spin_op(); for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(i); + spin_prod *= cudaq::spin::x(i); cudaq::spin_op spin_op = spin_prod; auto prod_op = cudaq::spin_operator::identity(); for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(i); + prod_op *= cudaq::spin_operator::x(i); cudaq::operator_sum op_sum = prod_op; std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } // addition inplace with product self (reverse order) - std::cout << std::endl << "addition inplace with product self (reverse order)" << std::endl; + std::cout << std::endl + << "addition inplace with product self (reverse order)" + << std::endl; for (auto nr_reps : repetitions) { auto spin_prod = cudaq::spin_op(); for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(100 - i); + spin_prod *= cudaq::spin::x(100 - i); cudaq::spin_op spin_op = spin_prod; auto prod_op = cudaq::spin_operator::identity(); for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(100 - i); + prod_op *= cudaq::spin_operator::x(100 - i); cudaq::operator_sum op_sum = prod_op; std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op += spin_prod; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum += prod_op; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } // product inplace with 2-term sum on fixed degrees - std::cout << std::endl << "product inplace with 2-term sum on fixed degrees" << std::endl; + std::cout << std::endl + << "product inplace with 2-term sum on fixed degrees" << std::endl; for (auto nr_reps : repetitions) { @@ -283,31 +292,30 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= spin_term; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= spin_term; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= prod_term; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= prod_term; + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } - - repetitions = {10, 20}; // we run out of memory if we go to 30 here - // product inplace with 2-term sum on varying degrees - std::cout << std::endl << "product inplace with 2-term sum on varying degrees" << std::endl; + std::cout << std::endl + << "product inplace with 2-term sum on varying degrees" + << std::endl; for (auto nr_reps : repetitions) { @@ -318,62 +326,68 @@ int main() { std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } // product inplace with 2-term sum on varying degrees (reverse order) - std::cout << std::endl << "product inplace with 2-term sum on varying degrees (reverse order)" << std::endl; + std::cout + << std::endl + << "product inplace with 2-term sum on varying degrees (reverse order)" + << std::endl; for (auto nr_reps : repetitions) { cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); // fixme: only const & constructor is public + cudaq::operator_sum op_sum = + cudaq::spin_operator::empty(); // fixme: only const & constructor is + // public op_sum = cudaq::spin_operator::identity(); std::cout << "nr ops: " << nr_reps << std::endl; if (run_old) { - /* - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - 1)); - stop = std::chrono::high_resolution_clock::now(); - duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - */ - std::cout << "Old setup segfaults" << std::endl; - } + /* + start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - + 1)); stop = std::chrono::high_resolution_clock::now(); duration = + std::chrono::duration(stop - start); std::cout << "Old setup took + " << duration.count() << " seconds.\n"; + */ + std::cout << "Old setup segfaults" << std::endl; + } if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(nr_reps - i) + cudaq::spin_operator::z(nr_reps - i - 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_reps; ++i) + op_sum *= (cudaq::spin_operator::x(nr_reps - i) + + cudaq::spin_operator::z(nr_reps - i - 1)); + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; } } // sum of products of random terms std::cout << std::endl << "sum of products of random terms" << std::endl; - std::vector nr_sum_terms = { 10, 100, 1000 }; - std::vector nr_product_terms = { 100, 1000, 10000 }; + std::vector nr_sum_terms = {10, 100, 1000}; + std::vector nr_product_terms = {100, 1000, 10000}; auto nr_degrees = 100; std::vector spin_ops; for (auto i = 0; i < nr_degrees; ++i) { @@ -394,43 +408,44 @@ int main() { for (auto nr_sums : nr_sum_terms) { for (auto nr_prods : nr_product_terms) { - std::vector> indices; + std::vector> indices; + for (auto i = 0; i < nr_sums; ++i) { + indices.push_back({}); + for (auto j = 0; j < nr_prods; ++j) + indices[i].push_back(rand() % 400); + } + + std::cout << "nr terms " << nr_sums << ", term length " << nr_prods + << std::endl; + + auto spin_op = cudaq::spin_op(); + auto op_sum = cudaq::spin_operator::empty(); + + if (run_old) { + auto start = std::chrono::high_resolution_clock::now(); for (auto i = 0; i < nr_sums; ++i) { - indices.push_back({}); - for (auto j = 0; j < nr_prods; ++j) - indices[i].push_back(rand() % 400); + auto term = cudaq::spin_op(); + for (auto j = 0; j < nr_prods; ++j) + term *= spin_ops[indices[i][j]]; + spin_op += term; } + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "Old setup took " << duration.count() << " seconds.\n"; + } - std::cout << "nr terms " << nr_sums << ", term length " << nr_prods << std::endl; - - auto spin_op = cudaq::spin_op(); - auto op_sum = cudaq::spin_operator::empty(); - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_sums; ++i) { - auto term = cudaq::spin_op(); - for (auto j = 0; j < nr_prods; ++j) - term *= spin_ops[indices[i][j]]; - spin_op += term; - } - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_sums; ++i) { - auto term = cudaq::spin_operator::identity(); - for (auto j = 0; j < nr_prods; ++j) - term *= leaf_ops[indices[i][j]]; - op_sum += term; - } - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; + if (run_new) { + auto start = std::chrono::high_resolution_clock::now(); + for (auto i = 0; i < nr_sums; ++i) { + auto term = cudaq::spin_operator::identity(); + for (auto j = 0; j < nr_prods; ++j) + term *= leaf_ops[indices[i][j]]; + op_sum += term; } + auto stop = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration(stop - start); + std::cout << "New setup took " << duration.count() << " seconds.\n"; + } } } return 0; diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 81fc5af12c..4eb43044c9 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -44,12 +44,11 @@ std::vector boson_operator::degrees() const { return {this->target}; } // constructors -boson_operator::boson_operator(int target) - : op_code(0), target(target) {} +boson_operator::boson_operator(int target) : op_code(0), target(target) {} -boson_operator::boson_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); +boson_operator::boson_operator(int target, int op_id) + : op_code(op_id), target(target) { + assert(0 <= op_id < 4); } // evaluations diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index 55e39499b9..160226b59f 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -21,8 +21,9 @@ template class product_operator; // FIXME: rename? -class boson_operator : public operator_handler{ -template friend class product_operator; +class boson_operator : public operator_handler { + template + friend class product_operator; private: // 0 = I, 1 = Ad (create), 2 = A (annihilate), 3 = AdA (number) @@ -34,10 +35,11 @@ template friend class product_operator; std::string op_code_to_string() const; public: - // FIXME: GET RID OF THIS #if !defined(NDEBUG) - static bool can_be_canonicalized; // needs to be false; no canonical order can be defined for matrix operator expressions + static bool + can_be_canonicalized; // needs to be false; no canonical order can be + // defined for matrix operator expressions #endif // read-only properties diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index 46432073ea..c238dc2e33 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -109,24 +109,29 @@ matrix_operator::matrix_operator(int degree) { this->targets.push_back(degree); } -matrix_operator::matrix_operator(std::string operator_id, const std::vector °rees) - : op_code(operator_id), targets(degrees) { - assert(this->targets.size() > 0); - } +matrix_operator::matrix_operator(std::string operator_id, + const std::vector °rees) + : op_code(operator_id), targets(degrees) { + assert(this->targets.size() > 0); +} -matrix_operator::matrix_operator(std::string operator_id, std::vector &°rees) - : op_code(operator_id), targets(std::move(degrees)) { - assert(this->targets.size() > 0); - } +matrix_operator::matrix_operator(std::string operator_id, + std::vector &°rees) + : op_code(operator_id), targets(std::move(degrees)) { + assert(this->targets.size() > 0); +} template , bool>> matrix_operator::matrix_operator(const T &other) { this->targets = other.degrees(); this->op_code = matrix_operator::type_prefix() + other.to_string(false); - if (matrix_operator::m_ops.find(this->op_code) == matrix_operator::m_ops.end()) { - auto func = [targets = other.degrees(), other] - (const std::vector &dimensions, const std::unordered_map> &_none) { + if (matrix_operator::m_ops.find(this->op_code) == + matrix_operator::m_ops.end()) { + auto func = [targets = other.degrees(), other]( + const std::vector &dimensions, + const std::unordered_map> + &_none) { std::unordered_map dims; for (auto i = 0; i < dimensions.size(); ++i) dims[targets[i]] = dimensions[i]; @@ -144,10 +149,10 @@ template matrix_operator::matrix_operator(const spin_operator &other); template matrix_operator::matrix_operator(const boson_operator &other); matrix_operator::matrix_operator(const matrix_operator &other) - : targets(other.targets), op_code(other.op_code) {} + : targets(other.targets), op_code(other.op_code) {} -matrix_operator::matrix_operator(matrix_operator &&other) - : targets(std::move(other.targets)), op_code(other.op_code) {} +matrix_operator::matrix_operator(matrix_operator &&other) + : targets(std::move(other.targets)), op_code(other.op_code) {} // assignments diff --git a/runtime/cudaq/dynamics/operator_sum.cpp b/runtime/cudaq/dynamics/operator_sum.cpp index 19dd745abd..e67ddcbeb5 100644 --- a/runtime/cudaq/dynamics/operator_sum.cpp +++ b/runtime/cudaq/dynamics/operator_sum.cpp @@ -22,26 +22,28 @@ namespace cudaq { // private methods -template +template void operator_sum::insert(const product_operator &other) { auto term_id = other.get_term_id(); auto it = this->term_map.find(term_id); if (it == this->term_map.cend()) { this->coefficients.push_back(other.coefficient); - this->term_map.insert(it, std::make_pair(std::move(term_id), this->terms.size())); + this->term_map.insert( + it, std::make_pair(std::move(term_id), this->terms.size())); this->terms.push_back(other.operators); } else { this->coefficients[it->second] += other.coefficient; } } -template +template void operator_sum::insert(product_operator &&other) { auto term_id = other.get_term_id(); auto it = this->term_map.find(term_id); if (it == this->term_map.cend()) { this->coefficients.push_back(std::move(other.coefficient)); - this->term_map.insert(it, std::make_pair(std::move(term_id), this->terms.size())); + this->term_map.insert( + it, std::make_pair(std::move(term_id), this->terms.size())); this->terms.push_back(std::move(other.operators)); } else { this->coefficients[it->second] += other.coefficient; @@ -68,21 +70,21 @@ operator_sum::m_evaluate(MatrixArithmetics arithmetics, auto degrees = this->degrees(); // We need to make sure all matrices are of the same size to sum them up. - auto paddedTerm = - [&arithmetics, °rees = std::as_const(degrees)](product_operator &&term) { - std::vector prod_ops; - prod_ops.reserve(degrees.size()); - auto term_degrees = term.degrees(); - for (auto degree : degrees) { - auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); - if (it == term_degrees.end()) { - HandlerTy identity(degree); - prod_ops.push_back(std::move(identity)); - } + auto paddedTerm = [&arithmetics, °rees = std::as_const(degrees)]( + product_operator &&term) { + std::vector prod_ops; + prod_ops.reserve(degrees.size()); + auto term_degrees = term.degrees(); + for (auto degree : degrees) { + auto it = std::find(term_degrees.begin(), term_degrees.end(), degree); + if (it == term_degrees.end()) { + HandlerTy identity(degree); + prod_ops.push_back(std::move(identity)); } - product_operator prod(1, std::move(prod_ops)); - prod *= term; // ensures canonical ordering - return prod; + } + product_operator prod(1, std::move(prod_ops)); + prod *= term; // ensures canonical ordering + return prod; }; if (pad_terms) { @@ -142,9 +144,9 @@ std::vector operator_sum::degrees() const { return std::move(degrees); } -template -int operator_sum::num_terms() const { - return this->terms.size(); +template +int operator_sum::num_terms() const { + return this->terms.size(); } template @@ -153,21 +155,20 @@ operator_sum::get_terms() const { std::vector> prods; prods.reserve(this->terms.size()); for (size_t i = 0; i < this->terms.size(); ++i) { - prods.push_back(product_operator(this->coefficients[i], this->terms[i])); + prods.push_back( + product_operator(this->coefficients[i], this->terms[i])); } - return std::move(prods); + return std::move(prods); } -#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ - \ - template \ - std::vector operator_sum::degrees() const; \ - \ - template \ - int operator_sum::num_terms() const; \ - \ - template \ - std::vector> operator_sum::get_terms() const; +#define INSTANTIATE_SUM_PROPERTIES(HandlerTy) \ + \ + template std::vector operator_sum::degrees() const; \ + \ + template int operator_sum::num_terms() const; \ + \ + template std::vector> \ + operator_sum::get_terms() const; INSTANTIATE_SUM_PROPERTIES(matrix_operator); INSTANTIATE_SUM_PROPERTIES(spin_operator); @@ -180,30 +181,40 @@ operator_sum::operator_sum(const product_operator &prod) { this->insert(prod); } -template -template, Args>...>::value, bool>> -operator_sum::operator_sum(Args&&... args) { +template +template , Args>...>::value, + bool>> +operator_sum::operator_sum(Args &&...args) { this->coefficients.reserve(sizeof...(Args)); this->term_map.reserve(sizeof...(Args)); this->terms.reserve(sizeof...(Args)); - aggregate_terms(std::forward&&>(args)...); + aggregate_terms(std::forward &&>(args)...); } -template -template::value && std::is_constructible::value, bool>> -operator_sum::operator_sum(const operator_sum &other) -: coefficients(other.coefficients) { +template +template ::value && + std::is_constructible::value, + bool>> +operator_sum::operator_sum(const operator_sum &other) + : coefficients(other.coefficients) { this->term_map.reserve(other.terms.size()); this->terms.reserve(other.terms.size()); for (const auto &operators : other.terms) { - product_operator term(product_operator(1., operators)); // coefficient does not matter - this->term_map.insert(this->term_map.cend(), std::make_pair(term.get_term_id(), this->terms.size())); + product_operator term( + product_operator(1., operators)); // coefficient does not matter + this->term_map.insert( + this->term_map.cend(), + std::make_pair(term.get_term_id(), this->terms.size())); this->terms.push_back(std::move(term.operators)); } } -template -operator_sum::operator_sum(const operator_sum &other, int size) { +template +operator_sum::operator_sum(const operator_sum &other, + int size) { if (size <= 0) { this->coefficients = other.coefficients; this->term_map = other.term_map; @@ -221,9 +232,10 @@ operator_sum::operator_sum(const operator_sum &other, int } } -template +template operator_sum::operator_sum(operator_sum &&other, int size) -: coefficients(std::move(other.coefficients)), term_map(std::move(other.term_map)), terms(std::move(other.terms)) { + : coefficients(std::move(other.coefficients)), + term_map(std::move(other.term_map)), terms(std::move(other.terms)) { if (size > 0) { this->coefficients.reserve(size); this->term_map.reserve(size); @@ -231,28 +243,28 @@ operator_sum::operator_sum(operator_sum &&other, int size) } } -#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ - \ - template \ - operator_sum::operator_sum(const product_operator &item2); \ - \ - template \ - operator_sum::operator_sum(product_operator &&item2); \ - \ - template \ - operator_sum::operator_sum(product_operator &&item1, \ - product_operator &&item2); \ - \ - template \ - operator_sum::operator_sum(product_operator &&item1, \ - product_operator &&item2, \ - product_operator &&item3); \ - \ - template \ - operator_sum::operator_sum(const operator_sum &other, int size); \ - \ - template \ - operator_sum::operator_sum(operator_sum &&other, int size); +#define INSTANTIATE_SUM_CONSTRUCTORS(HandlerTy) \ + \ + template operator_sum::operator_sum( \ + const product_operator &item2); \ + \ + template operator_sum::operator_sum( \ + product_operator &&item2); \ + \ + template operator_sum::operator_sum( \ + product_operator &&item1, \ + product_operator &&item2); \ + \ + template operator_sum::operator_sum( \ + product_operator &&item1, \ + product_operator &&item2, \ + product_operator &&item3); \ + \ + template operator_sum::operator_sum( \ + const operator_sum &other, int size); \ + \ + template operator_sum::operator_sum( \ + operator_sum &&other, int size); template operator_sum::operator_sum( const operator_sum &other); @@ -265,31 +277,39 @@ INSTANTIATE_SUM_CONSTRUCTORS(boson_operator); // assignments -template - template::value && std::is_constructible::value, bool>> -operator_sum& operator_sum::operator=(const product_operator &other) { +template +template ::value && + std::is_constructible::value, + bool>> +operator_sum & +operator_sum::operator=(const product_operator &other) { *this = product_operator(other); return *this; } -template -operator_sum& operator_sum::operator=(const product_operator &other) { +template +operator_sum & +operator_sum::operator=(const product_operator &other) { this->coefficients.clear(); this->term_map.clear(); this->terms.clear(); this->coefficients.push_back(other.coefficient); - this->term_map.insert(this->term_map.cend(), std::make_pair(other.get_term_id(), 0)); + this->term_map.insert(this->term_map.cend(), + std::make_pair(other.get_term_id(), 0)); this->terms.push_back(other.operators); return *this; } -template -operator_sum& operator_sum::operator=(product_operator &&other) { +template +operator_sum & +operator_sum::operator=(product_operator &&other) { this->coefficients.clear(); this->term_map.clear(); this->terms.clear(); this->coefficients.push_back(std::move(other.coefficient)); - this->term_map.insert(this->term_map.cend(), std::make_pair(other.get_term_id(), 0)); + this->term_map.insert(this->term_map.cend(), + std::make_pair(other.get_term_id(), 0)); this->terms.push_back(std::move(other.operators)); return *this; } @@ -370,10 +390,13 @@ std::string operator_sum::to_string() const { return std::move(str); } -template -matrix_2 operator_sum::to_matrix(std::unordered_map dimensions, - const std::unordered_map> ¶meters) const { - return std::move(m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix()); +template +matrix_2 operator_sum::to_matrix( + std::unordered_map dimensions, + const std::unordered_map> ¶meters) + const { + return std::move( + m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix()); } #define INSTANTIATE_SUM_EVALUATIONS(HandlerTy) \ @@ -419,19 +442,17 @@ operator_sum operator_sum::operator+() && { return std::move(*this); } -#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ - \ - template \ - operator_sum operator_sum::operator-() const &; \ - \ - template \ - operator_sum operator_sum::operator-() &&; \ - \ - template \ - operator_sum operator_sum::operator+() const &; \ - \ - template \ - operator_sum operator_sum::operator+() &&; \ +#define INSTANTIATE_SUM_UNARY_OPS(HandlerTy) \ + \ + template operator_sum operator_sum::operator-() \ + const &; \ + \ + template operator_sum operator_sum::operator-() &&; \ + \ + template operator_sum operator_sum::operator+() \ + const &; \ + \ + template operator_sum operator_sum::operator+() &&; INSTANTIATE_SUM_UNARY_OPS(matrix_operator); INSTANTIATE_SUM_UNARY_OPS(spin_operator); @@ -439,44 +460,48 @@ INSTANTIATE_SUM_UNARY_OPS(boson_operator); // right-hand arithmetics -#define SUM_MULTIPLICATION(otherTy) \ - \ - template \ - operator_sum operator_sum::operator*(otherTy other) const & { \ - operator_sum sum; \ - sum.coefficients.reserve(this->coefficients.size()); \ - sum.term_map = this->term_map; \ - sum.terms = this->terms; \ - for (const auto &coeff : this->coefficients) \ - sum.coefficients.push_back(coeff * other); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator_sum::operator*(otherTy other) && { \ - for (auto &coeff : this->coefficients) \ - coeff *= other; \ - return std::move(*this); \ +#define SUM_MULTIPLICATION(otherTy) \ + \ + template \ + operator_sum operator_sum::operator*(otherTy other) \ + const & { \ + operator_sum sum; \ + sum.coefficients.reserve(this->coefficients.size()); \ + sum.term_map = this->term_map; \ + sum.terms = this->terms; \ + for (const auto &coeff : this->coefficients) \ + sum.coefficients.push_back(coeff *other); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator*( \ + otherTy other) && { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return std::move(*this); \ } SUM_MULTIPLICATION(double); SUM_MULTIPLICATION(std::complex); SUM_MULTIPLICATION(const scalar_operator &); -#define SUM_ADDITION(otherTy, op) \ - \ - template \ - operator_sum operator_sum::operator op(otherTy other) const & { \ - operator_sum sum(*this, this->terms.size() + 1); \ - sum.insert(product_operator(op other)); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator_sum::operator op(otherTy other) && { \ - this->insert(product_operator(op other)); \ - return std::move(*this); \ - } \ +#define SUM_ADDITION(otherTy, op) \ + \ + template \ + operator_sum operator_sum::operator op(otherTy other) \ + const & { \ + operator_sum sum(*this, this->terms.size() + 1); \ + sum.insert(product_operator(op other)); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + otherTy other) && { \ + this->insert(product_operator(op other)); \ + return std::move(*this); \ + } SUM_ADDITION(double, +); SUM_ADDITION(double, -); @@ -485,58 +510,60 @@ SUM_ADDITION(std::complex, -); SUM_ADDITION(const scalar_operator &, +); SUM_ADDITION(const scalar_operator &, -); -#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ - \ - template \ - operator_sum operator_sum::operator*(double other) const &; \ - template \ - operator_sum operator_sum::operator*(double other) &&; \ - template \ - operator_sum operator_sum::operator+(double other) const &; \ - template \ - operator_sum operator_sum::operator+(double other) &&; \ - template \ - operator_sum operator_sum::operator-(double other) const &; \ - template \ - operator_sum operator_sum::operator-(double other) &&; \ - template \ - operator_sum operator_sum::operator*(std::complex other) const &; \ - template \ - operator_sum operator_sum::operator*(std::complex other) &&; \ - template \ - operator_sum operator_sum::operator+(std::complex other) const &; \ - template \ - operator_sum operator_sum::operator+(std::complex other) &&; \ - template \ - operator_sum operator_sum::operator-(std::complex other) const &; \ - template \ - operator_sum operator_sum::operator-(std::complex other) &&; \ - template \ - operator_sum operator_sum::operator*(const scalar_operator &other) const &; \ - template \ - operator_sum operator_sum::operator*(const scalar_operator &other) &&; \ - template \ - operator_sum operator_sum::operator+(const scalar_operator &other) const &; \ - template \ - operator_sum operator_sum::operator+(const scalar_operator &other) &&; \ - template \ - operator_sum operator_sum::operator-(const scalar_operator &other) const &; \ - template \ - operator_sum operator_sum::operator-(const scalar_operator &other) &&; +#define INSTANTIATE_SUM_RHSIMPLE_OPS(HandlerTy) \ + \ + template operator_sum operator_sum::operator*( \ + double other) const &; \ + template operator_sum operator_sum::operator*( \ + double other) &&; \ + template operator_sum operator_sum::operator+( \ + double other) const &; \ + template operator_sum operator_sum::operator+( \ + double other) &&; \ + template operator_sum operator_sum::operator-( \ + double other) const &; \ + template operator_sum operator_sum::operator-( \ + double other) &&; \ + template operator_sum operator_sum::operator*( \ + std::complex other) const &; \ + template operator_sum operator_sum::operator*( \ + std::complex other) &&; \ + template operator_sum operator_sum::operator+( \ + std::complex other) const &; \ + template operator_sum operator_sum::operator+( \ + std::complex other) &&; \ + template operator_sum operator_sum::operator-( \ + std::complex other) const &; \ + template operator_sum operator_sum::operator-( \ + std::complex other) &&; \ + template operator_sum operator_sum::operator*( \ + const scalar_operator &other) const &; \ + template operator_sum operator_sum::operator*( \ + const scalar_operator &other) &&; \ + template operator_sum operator_sum::operator+( \ + const scalar_operator &other) const &; \ + template operator_sum operator_sum::operator+( \ + const scalar_operator &other) &&; \ + template operator_sum operator_sum::operator-( \ + const scalar_operator &other) const &; \ + template operator_sum operator_sum::operator-( \ + const scalar_operator &other) &&; INSTANTIATE_SUM_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(spin_operator); INSTANTIATE_SUM_RHSIMPLE_OPS(boson_operator); template -operator_sum operator_sum::operator*(const product_operator &other) const { +operator_sum operator_sum::operator*( + const product_operator &other) const { operator_sum sum; // the entire sum needs to be rebuilt sum.coefficients.reserve(this->coefficients.size()); sum.term_map.reserve(this->terms.size()); sum.terms.reserve(this->terms.size()); for (auto i = 0; i < this->terms.size(); ++i) { auto max_size = this->terms[i].size() + other.operators.size(); - product_operator prod(this->coefficients[i] * other.coefficient, this->terms[i], max_size); + product_operator prod(this->coefficients[i] * other.coefficient, + this->terms[i], max_size); for (HandlerTy op : other.operators) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -544,43 +571,44 @@ operator_sum operator_sum::operator*(const product_operato return std::move(sum); } -#define SUM_ADDITION_PRODUCT(op) \ - \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) const & { \ - operator_sum sum(*this, this->terms.size() + 1); \ - sum.insert(op other); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator_sum::operator op( \ - const product_operator &other) && { \ - this->insert(op other); \ - return std::move(*this); \ - } \ - \ - template \ - operator_sum operator_sum::operator op( \ - product_operator &&other) const & { \ - operator_sum sum(*this, this->terms.size() + 1); \ - sum.insert(op std::move(other)); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator_sum::operator op( \ - product_operator &&other) && { \ - this->insert(op std::move(other)); \ - return std::move(*this); \ - } \ +#define SUM_ADDITION_PRODUCT(op) \ + \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) const & { \ + operator_sum sum(*this, this->terms.size() + 1); \ + sum.insert(op other); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + const product_operator &other) && { \ + this->insert(op other); \ + return std::move(*this); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + product_operator &&other) const & { \ + operator_sum sum(*this, this->terms.size() + 1); \ + sum.insert(op std::move(other)); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + product_operator &&other) && { \ + this->insert(op std::move(other)); \ + return std::move(*this); \ + } SUM_ADDITION_PRODUCT(+) SUM_ADDITION_PRODUCT(-) template -operator_sum operator_sum::operator*(const operator_sum &other) const { +operator_sum +operator_sum::operator*(const operator_sum &other) const { operator_sum sum; // the entire sum needs to be rebuilt auto max_size = this->terms.size() * other.terms.size(); sum.coefficients.reserve(max_size); @@ -589,7 +617,9 @@ operator_sum operator_sum::operator*(const operator_sumterms.size(); ++i) { for (auto j = 0; j < other.terms.size(); ++j) { auto max_size = this->terms[i].size() + other.terms[j].size(); - product_operator prod(this->coefficients[i] * other.coefficients[j], this->terms[i], max_size); + product_operator prod(this->coefficients[i] * + other.coefficients[j], + this->terms[i], max_size); for (HandlerTy op : other.terms[j]) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -598,139 +628,126 @@ operator_sum operator_sum::operator*(const operator_sum \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) const & { \ - operator_sum sum(*this, this->terms.size() + other.terms.size()); \ - for (auto i = 0; i < other.terms.size(); ++i) { \ - product_operator prod(op other.coefficients[i], other.terms[i]); \ - sum.insert(std::move(prod)); \ - } \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator_sum::operator op( \ - const operator_sum &other) && { \ - auto max_size = this->terms.size() + other.terms.size(); \ - this->coefficients.reserve(max_size); \ - this->term_map.reserve(max_size); \ - this->terms.reserve(max_size); \ - for (auto i = 0; i < other.terms.size(); ++i) \ - this->insert(product_operator( \ - op other.coefficients[i], other.terms[i])); \ - return std::move(*this); \ - } \ - \ - template \ - operator_sum operator_sum::operator op( \ - operator_sum &&other) const & { \ - operator_sum sum(*this, this->terms.size() + other.terms.size()); \ - for (auto i = 0; i < other.terms.size(); ++i) { \ - product_operator prod( \ - op std::move(other.coefficients[i]), std::move(other.terms[i])); \ - sum.insert(std::move(prod)); \ - } \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator_sum::operator op( \ - operator_sum &&other) && { \ - auto max_size = this->terms.size() + other.terms.size(); \ - this->coefficients.reserve(max_size); \ - this->term_map.reserve(max_size); \ - this->terms.reserve(max_size); \ - for (auto i = 0; i < other.terms.size(); ++i) \ - this->insert(product_operator( \ - op std::move(other.coefficients[i]), std::move(other.terms[i]))); \ - return std::move(*this); \ - } \ +#define SUM_ADDITION_SUM(op) \ + \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) const & { \ + operator_sum sum(*this, \ + this->terms.size() + other.terms.size()); \ + for (auto i = 0; i < other.terms.size(); ++i) { \ + product_operator prod(op other.coefficients[i], \ + other.terms[i]); \ + sum.insert(std::move(prod)); \ + } \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + const operator_sum &other) && { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator(op other.coefficients[i], \ + other.terms[i])); \ + return std::move(*this); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + operator_sum &&other) const & { \ + operator_sum sum(*this, \ + this->terms.size() + other.terms.size()); \ + for (auto i = 0; i < other.terms.size(); ++i) { \ + product_operator prod(op std::move(other.coefficients[i]), \ + std::move(other.terms[i])); \ + sum.insert(std::move(prod)); \ + } \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator_sum::operator op( \ + operator_sum &&other) && { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator( \ + op std::move(other.coefficients[i]), std::move(other.terms[i]))); \ + return std::move(*this); \ + } SUM_ADDITION_SUM(+); SUM_ADDITION_SUM(-); -#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - operator_sum operator_sum::operator*( \ - const product_operator &other) const; \ - template \ - operator_sum operator_sum::operator+( \ - const product_operator &other) const &; \ - template \ - operator_sum operator_sum::operator+( \ - const product_operator &other) &&; \ - template \ - operator_sum operator_sum::operator+( \ - product_operator &&other) const &; \ - template \ - operator_sum operator_sum::operator+( \ - product_operator &&other) &&; \ - template \ - operator_sum operator_sum::operator-( \ - const product_operator &other) const &; \ - template \ - operator_sum operator_sum::operator-( \ - const product_operator &other) &&; \ - template \ - operator_sum operator_sum::operator-( \ - product_operator &&other) const &; \ - template \ - operator_sum operator_sum::operator-( \ - product_operator &&other) &&; \ - template \ - operator_sum operator_sum::operator*( \ - const operator_sum &other) const; \ - template \ - operator_sum operator_sum::operator+( \ - const operator_sum &other) const &; \ - template \ - operator_sum operator_sum::operator+( \ - const operator_sum &other) &&; \ - template \ - operator_sum operator_sum::operator+( \ - operator_sum &&other) const &; \ - template \ - operator_sum operator_sum::operator+( \ - operator_sum &&other) &&; \ - template \ - operator_sum operator_sum::operator-( \ - const operator_sum &other) const &; \ - template \ - operator_sum operator_sum::operator-( \ - const operator_sum &other) &&; \ - template \ - operator_sum operator_sum::operator-( \ - operator_sum &&other) const &; \ - template \ - operator_sum operator_sum::operator-( \ - operator_sum &&other) &&; \ +#define INSTANTIATE_SUM_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template operator_sum operator_sum::operator*( \ + const product_operator &other) const; \ + template operator_sum operator_sum::operator+( \ + const product_operator &other) const &; \ + template operator_sum operator_sum::operator+( \ + const product_operator &other) &&; \ + template operator_sum operator_sum::operator+( \ + product_operator &&other) const &; \ + template operator_sum operator_sum::operator+( \ + product_operator &&other) &&; \ + template operator_sum operator_sum::operator-( \ + const product_operator &other) const &; \ + template operator_sum operator_sum::operator-( \ + const product_operator &other) &&; \ + template operator_sum operator_sum::operator-( \ + product_operator &&other) const &; \ + template operator_sum operator_sum::operator-( \ + product_operator &&other) &&; \ + template operator_sum operator_sum::operator*( \ + const operator_sum &other) const; \ + template operator_sum operator_sum::operator+( \ + const operator_sum &other) const &; \ + template operator_sum operator_sum::operator+( \ + const operator_sum &other) &&; \ + template operator_sum operator_sum::operator+( \ + operator_sum &&other) const &; \ + template operator_sum operator_sum::operator+( \ + operator_sum &&other) &&; \ + template operator_sum operator_sum::operator-( \ + const operator_sum &other) const &; \ + template operator_sum operator_sum::operator-( \ + const operator_sum &other) &&; \ + template operator_sum operator_sum::operator-( \ + operator_sum &&other) const &; \ + template operator_sum operator_sum::operator-( \ + operator_sum &&other) &&; INSTANTIATE_SUM_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(spin_operator); INSTANTIATE_SUM_RHCOMPOSITE_OPS(boson_operator); -#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ - template \ - operator_sum& operator_sum::operator*=(otherTy other) { \ - for (auto &coeff : this->coefficients) \ - coeff *= other; \ - return *this; \ - } \ +#define SUM_MULTIPLICATION_ASSIGNMENT(otherTy) \ + template \ + operator_sum &operator_sum::operator*=( \ + otherTy other) { \ + for (auto &coeff : this->coefficients) \ + coeff *= other; \ + return *this; \ + } SUM_MULTIPLICATION_ASSIGNMENT(double); SUM_MULTIPLICATION_ASSIGNMENT(std::complex); SUM_MULTIPLICATION_ASSIGNMENT(const scalar_operator &); -#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ - template \ - operator_sum& operator_sum::operator op##=(otherTy other) { \ - this->insert(product_operator(op other)); \ - return *this; \ - } \ +#define SUM_ADDITION_ASSIGNMENT(otherTy, op) \ + template \ + operator_sum &operator_sum::operator op##=( \ + otherTy other) { \ + this->insert(product_operator(op other)); \ + return *this; \ + } SUM_ADDITION_ASSIGNMENT(double, +); SUM_ADDITION_ASSIGNMENT(double, -); @@ -740,14 +757,16 @@ SUM_ADDITION_ASSIGNMENT(const scalar_operator &, +); SUM_ADDITION_ASSIGNMENT(const scalar_operator &, -); template -operator_sum& operator_sum::operator*=(const product_operator &other) { +operator_sum & +operator_sum::operator*=(const product_operator &other) { operator_sum sum; sum.coefficients.reserve(this->coefficients.size()); sum.term_map.reserve(this->terms.size()); sum.terms.reserve(this->terms.size()); for (auto i = 0; i < this->terms.size(); ++i) { auto max_size = this->terms[i].size() + other.operators.size(); - product_operator prod(this->coefficients[i] * other.coefficient, this->terms[i], max_size); + product_operator prod(this->coefficients[i] * other.coefficient, + this->terms[i], max_size); for (HandlerTy op : other.operators) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -756,27 +775,28 @@ operator_sum& operator_sum::operator*=(const product_opera return *this; } -#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ - \ - template \ - operator_sum& operator_sum::operator op##=( \ - const product_operator &other) { \ - this->insert(op other); \ - return *this; \ - } \ - \ - template \ - operator_sum& operator_sum::operator op##=( \ - product_operator &&other) { \ - this->insert(op std::move(other)); \ - return *this; \ - } \ +#define SUM_ADDITION_PRODUCT_ASSIGNMENT(op) \ + \ + template \ + operator_sum &operator_sum::operator op##=( \ + const product_operator &other) { \ + this->insert(op other); \ + return *this; \ + } \ + \ + template \ + operator_sum &operator_sum::operator op##=( \ + product_operator &&other) { \ + this->insert(op std::move(other)); \ + return *this; \ + } SUM_ADDITION_PRODUCT_ASSIGNMENT(+) SUM_ADDITION_PRODUCT_ASSIGNMENT(-) template -operator_sum& operator_sum::operator*=(const operator_sum &other) { +operator_sum & +operator_sum::operator*=(const operator_sum &other) { operator_sum sum; // the entire sum needs to be rebuilt auto max_size = this->terms.size() * other.terms.size(); sum.coefficients.reserve(max_size); @@ -785,7 +805,9 @@ operator_sum& operator_sum::operator*=(const operator_sum< for (auto i = 0; i < this->terms.size(); ++i) { for (auto j = 0; j < other.terms.size(); ++j) { auto max_size = this->terms[i].size() + other.terms[j].size(); - product_operator prod(this->coefficients[i] * other.coefficients[j], this->terms[i], max_size); + product_operator prod(this->coefficients[i] * + other.coefficients[j], + this->terms[i], max_size); for (HandlerTy op : other.terms[j]) prod.insert(std::move(op)); sum.insert(std::move(prod)); @@ -795,77 +817,77 @@ operator_sum& operator_sum::operator*=(const operator_sum< return *this; } -#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ - \ - template \ - operator_sum& operator_sum::operator op##=( \ - const operator_sum &other) { \ - auto max_size = this->terms.size() + other.terms.size(); \ - this->coefficients.reserve(max_size); \ - this->term_map.reserve(max_size); \ - this->terms.reserve(max_size); \ - for (auto i = 0; i < other.terms.size(); ++i) \ - this->insert(product_operator( \ - op other.coefficients[i], other.terms[i])); \ - return *this; \ - } \ - \ - template \ - operator_sum& operator_sum::operator op##=( \ - operator_sum &&other) { \ - auto max_size = this->terms.size() + other.terms.size(); \ - this->coefficients.reserve(max_size); \ - this->term_map.reserve(max_size); \ - this->terms.reserve(max_size); \ - for (auto i = 0; i < other.terms.size(); ++i) \ - this->insert(product_operator( \ - op std::move(other.coefficients[i]), std::move(other.terms[i]))); \ - return *this; \ - } \ +#define SUM_ADDITION_SUM_ASSIGNMENT(op) \ + \ + template \ + operator_sum &operator_sum::operator op##=( \ + const operator_sum &other) { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator(op other.coefficients[i], \ + other.terms[i])); \ + return *this; \ + } \ + \ + template \ + operator_sum &operator_sum::operator op##=( \ + operator_sum &&other) { \ + auto max_size = this->terms.size() + other.terms.size(); \ + this->coefficients.reserve(max_size); \ + this->term_map.reserve(max_size); \ + this->terms.reserve(max_size); \ + for (auto i = 0; i < other.terms.size(); ++i) \ + this->insert(product_operator( \ + op std::move(other.coefficients[i]), std::move(other.terms[i]))); \ + return *this; \ + } SUM_ADDITION_SUM_ASSIGNMENT(+); SUM_ADDITION_SUM_ASSIGNMENT(-); -#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ - \ - template \ - operator_sum& operator_sum::operator*=(double other); \ - template \ - operator_sum& operator_sum::operator+=(double other); \ - template \ - operator_sum& operator_sum::operator-=(double other); \ - template \ - operator_sum& operator_sum::operator*=(std::complex other); \ - template \ - operator_sum& operator_sum::operator+=(std::complex other); \ - template \ - operator_sum& operator_sum::operator-=(std::complex other); \ - template \ - operator_sum& operator_sum::operator*=(const scalar_operator &other); \ - template \ - operator_sum& operator_sum::operator+=(const scalar_operator &other); \ - template \ - operator_sum& operator_sum::operator-=(const scalar_operator &other); \ - template \ - operator_sum& operator_sum::operator*=(const product_operator &other); \ - template \ - operator_sum& operator_sum::operator+=(const product_operator &other); \ - template \ - operator_sum& operator_sum::operator+=(product_operator &&other); \ - template \ - operator_sum& operator_sum::operator-=(const product_operator &other); \ - template \ - operator_sum& operator_sum::operator-=(product_operator &&other); \ - template \ - operator_sum& operator_sum::operator*=(const operator_sum &other); \ - template \ - operator_sum& operator_sum::operator+=(const operator_sum &other); \ - template \ - operator_sum& operator_sum::operator+=(operator_sum &&other); \ - template \ - operator_sum& operator_sum::operator-=(const operator_sum &other); \ - template \ - operator_sum& operator_sum::operator-=(operator_sum &&other); \ +#define INSTANTIATE_SUM_OPASSIGNMENTS(HandlerTy) \ + \ + template operator_sum &operator_sum::operator*=( \ + double other); \ + template operator_sum &operator_sum::operator+=( \ + double other); \ + template operator_sum &operator_sum::operator-=( \ + double other); \ + template operator_sum &operator_sum::operator*=( \ + std::complex other); \ + template operator_sum &operator_sum::operator+=( \ + std::complex other); \ + template operator_sum &operator_sum::operator-=( \ + std::complex other); \ + template operator_sum &operator_sum::operator*=( \ + const scalar_operator &other); \ + template operator_sum &operator_sum::operator+=( \ + const scalar_operator &other); \ + template operator_sum &operator_sum::operator-=( \ + const scalar_operator &other); \ + template operator_sum &operator_sum::operator*=( \ + const product_operator &other); \ + template operator_sum &operator_sum::operator+=( \ + const product_operator &other); \ + template operator_sum &operator_sum::operator+=( \ + product_operator &&other); \ + template operator_sum &operator_sum::operator-=( \ + const product_operator &other); \ + template operator_sum &operator_sum::operator-=( \ + product_operator &&other); \ + template operator_sum &operator_sum::operator*=( \ + const operator_sum &other); \ + template operator_sum &operator_sum::operator+=( \ + const operator_sum &other); \ + template operator_sum &operator_sum::operator+=( \ + operator_sum &&other); \ + template operator_sum &operator_sum::operator-=( \ + const operator_sum &other); \ + template operator_sum &operator_sum::operator-=( \ + operator_sum &&other); INSTANTIATE_SUM_OPASSIGNMENTS(matrix_operator); INSTANTIATE_SUM_OPASSIGNMENTS(spin_operator); @@ -873,50 +895,50 @@ INSTANTIATE_SUM_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define SUM_MULTIPLICATION_REVERSE(otherTy) \ - \ - template \ - operator_sum operator*(otherTy other, \ - const operator_sum &self) { \ - operator_sum sum; \ - sum.coefficients.reserve(self.coefficients.size()); \ - sum.terms = self.terms; \ - sum.term_map = self.term_map; \ - for (const auto &coeff : self.coefficients) \ - sum.coefficients.push_back(coeff * other); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator*(otherTy other, \ - operator_sum &&self) { \ - for (auto &&coeff : self.coefficients) \ - coeff *= other; \ - return std::move(self); \ - } \ +#define SUM_MULTIPLICATION_REVERSE(otherTy) \ + \ + template \ + operator_sum operator*(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum; \ + sum.coefficients.reserve(self.coefficients.size()); \ + sum.terms = self.terms; \ + sum.term_map = self.term_map; \ + for (const auto &coeff : self.coefficients) \ + sum.coefficients.push_back(coeff *other); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator*(otherTy other, \ + operator_sum &&self) { \ + for (auto &&coeff : self.coefficients) \ + coeff *= other; \ + return std::move(self); \ + } SUM_MULTIPLICATION_REVERSE(double); SUM_MULTIPLICATION_REVERSE(std::complex); SUM_MULTIPLICATION_REVERSE(const scalar_operator &); -#define SUM_ADDITION_REVERSE(otherTy, op) \ - \ - template \ - operator_sum operator op(otherTy other, \ - const operator_sum &self) { \ - operator_sum sum(op self); \ - sum.insert(product_operator(other)); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum operator op(otherTy other, \ - operator_sum &&self) { \ - for (auto &&coeff : self.coefficients) \ - coeff = std::move(op coeff); \ - self.insert(product_operator(other)); \ - return std::move(self); \ - } \ +#define SUM_ADDITION_REVERSE(otherTy, op) \ + \ + template \ + operator_sum operator op(otherTy other, \ + const operator_sum &self) { \ + operator_sum sum(op self); \ + sum.insert(product_operator(other)); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum operator op(otherTy other, \ + operator_sum &&self) { \ + for (auto &&coeff : self.coefficients) \ + coeff = std::move(op coeff); \ + self.insert(product_operator(other)); \ + return std::move(self); \ + } SUM_ADDITION_REVERSE(double, +); SUM_ADDITION_REVERSE(double, -); @@ -925,44 +947,44 @@ SUM_ADDITION_REVERSE(std::complex, -); SUM_ADDITION_REVERSE(const scalar_operator &, +); SUM_ADDITION_REVERSE(const scalar_operator &, -); -#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - operator_sum operator*(double other, const operator_sum &self); \ - template \ - operator_sum operator*(double other, operator_sum &&self); \ - template \ - operator_sum operator+(double other, const operator_sum &self); \ - template \ - operator_sum operator+(double other, operator_sum &&self); \ - template \ - operator_sum operator-(double other, const operator_sum &self); \ - template \ - operator_sum operator-(double other, operator_sum &&self); \ - template \ - operator_sum operator*(std::complex other, const operator_sum &self); \ - template \ - operator_sum operator*(std::complex other, operator_sum &&self); \ - template \ - operator_sum operator+(std::complex other, const operator_sum &self); \ - template \ - operator_sum operator+(std::complex other, operator_sum &&self); \ - template \ - operator_sum operator-(std::complex other, const operator_sum &self); \ - template \ - operator_sum operator-(std::complex other, operator_sum &&self); \ - template \ - operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ - template \ - operator_sum operator*(const scalar_operator &other, operator_sum &&self); \ - template \ - operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ - template \ - operator_sum operator+(const scalar_operator &other, operator_sum &&self); \ - template \ - operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ - template \ - operator_sum operator-(const scalar_operator &other, operator_sum &&self); \ +#define INSTANTIATE_SUM_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template operator_sum operator*( \ + double other, const operator_sum &self); \ + template operator_sum operator*(double other, \ + operator_sum &&self); \ + template operator_sum operator+( \ + double other, const operator_sum &self); \ + template operator_sum operator+(double other, \ + operator_sum &&self); \ + template operator_sum operator-( \ + double other, const operator_sum &self); \ + template operator_sum operator-(double other, \ + operator_sum &&self); \ + template operator_sum operator*( \ + std::complex other, const operator_sum &self); \ + template operator_sum operator*(std::complex other, \ + operator_sum &&self); \ + template operator_sum operator+( \ + std::complex other, const operator_sum &self); \ + template operator_sum operator+(std::complex other, \ + operator_sum &&self); \ + template operator_sum operator-( \ + std::complex other, const operator_sum &self); \ + template operator_sum operator-(std::complex other, \ + operator_sum &&self); \ + template operator_sum operator*( \ + const scalar_operator &other, const operator_sum &self); \ + template operator_sum operator*(const scalar_operator &other, \ + operator_sum &&self); \ + template operator_sum operator+( \ + const scalar_operator &other, const operator_sum &self); \ + template operator_sum operator+(const scalar_operator &other, \ + operator_sum &&self); \ + template operator_sum operator-( \ + const scalar_operator &other, const operator_sum &self); \ + template operator_sum operator-(const scalar_operator &other, \ + operator_sum &&self); INSTANTIATE_SUM_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_SUM_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index 97d4113413..b5e83ee724 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -43,50 +43,74 @@ bool product_operator::is_canonicalized() const { } #endif -template -std::vector::const_iterator product_operator::find_insert_at(const HandlerTy &other) const { - // the logic below just ensures that terms are fully or partially ordered in canonical order - - // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers - return std::find_if(this->operators.crbegin(), this->operators.crend(), - [other_target = other.target] (const HandlerTy& self_op) { - return other_target <= self_op.target; // FIXME: relies on canonical order - }).base(); // base causes insert after for reverse iterator -} - -template<> -std::vector::const_iterator product_operator::find_insert_at(const matrix_operator &other) const { - // the logic below just ensures that terms are fully or partially ordered in canonical order - - // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers - return std::find_if(this->operators.crbegin(), this->operators.crend(), - [&other_degrees = static_cast&>(other.degrees())] - (const matrix_operator& self_op) { - const std::vector &self_op_degrees = self_op.degrees(); - for (auto other_degree : other_degrees) { - auto item_it = std::find_if(self_op_degrees.crbegin(), self_op_degrees.crend(), - [other_degree](int self_degree) { return other_degree <= self_degree; }); // FIXME: relies on canonical order - if (item_it != self_op_degrees.crend()) return true; - } - return false; - }).base(); // base causes insert after for reverse iterator +template +std::vector::const_iterator +product_operator::find_insert_at(const HandlerTy &other) const { + // the logic below just ensures that terms are fully or partially ordered in + // canonical order - a best effort is made to order terms, but a full + // canonical ordering is not possible for certain handlers + return std::find_if( + this->operators.crbegin(), this->operators.crend(), + [other_target = other.target](const HandlerTy &self_op) { + return other_target <= + self_op.target; // FIXME: relies on canonical order + }) + .base(); // base causes insert after for reverse iterator +} + +template <> +std::vector::const_iterator +product_operator::find_insert_at( + const matrix_operator &other) const { + // the logic below just ensures that terms are fully or partially ordered in + // canonical order - a best effort is made to order terms, but a full + // canonical ordering is not possible for certain handlers + return std::find_if( + this->operators.crbegin(), this->operators.crend(), + [&other_degrees = static_cast &>( + other.degrees())](const matrix_operator &self_op) { + const std::vector &self_op_degrees = self_op.degrees(); + for (auto other_degree : other_degrees) { + auto item_it = std::find_if( + self_op_degrees.crbegin(), self_op_degrees.crend(), + [other_degree](int self_degree) { + return other_degree <= self_degree; + }); // FIXME: relies on canonical order + if (item_it != self_op_degrees.crend()) + return true; + } + return false; + }) + .base(); // base causes insert after for reverse iterator } -template -template::value && !product_operator::supports_inplace_mult, int>> -void product_operator::insert(T &&other) { +template +template ::value && + !product_operator::supports_inplace_mult, + int>> +void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); this->operators.insert(pos, other); } -template -template ::value && product_operator::supports_inplace_mult, bool>> +template +template ::value && + product_operator::supports_inplace_mult, + bool>> void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); - if (pos != this->operators.begin() && (pos - 1)->target == other.target) - this->coefficient *= this->operators.erase(pos - 1, pos - 1)->inplace_mult(other); // erase: constant time conversion to non-const iterator - else this->operators.insert(pos, std::move(other)); + if (pos != this->operators.begin() && (pos - 1)->target == other.target) + this->coefficient *= + this->operators.erase(pos - 1, pos - 1) + ->inplace_mult( + other); // erase: constant time conversion to non-const iterator + else + this->operators.insert(pos, std::move(other)); } -template +template std::string product_operator::get_term_id() const { std::string term_id; for (const auto &op : this->operators) @@ -97,9 +121,10 @@ std::string product_operator::get_term_id() const { template void product_operator::aggregate_terms() {} -template -template -void product_operator::aggregate_terms(HandlerTy &&head, Args&& ... args) { +template +template +void product_operator::aggregate_terms(HandlerTy &&head, + Args &&...args) { this->insert(std::forward(head)); aggregate_terms(std::forward(args)...); } @@ -237,11 +262,11 @@ INSTANTIATE_PRODUCT_PROPERTIES(boson_operator); template product_operator::product_operator(double coefficient) - : coefficient(coefficient) {} + : coefficient(coefficient) {} template product_operator::product_operator(HandlerTy &&atomic) - : coefficient(1.) { + : coefficient(1.) { this->operators.push_back(std::move(atomic)); assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); // relevant for custom matrix operators @@ -257,34 +282,44 @@ product_operator::product_operator(scalar_operator coefficient, : coefficient(std::move(coefficient)) { this->operators.reserve(sizeof...(Args)); aggregate_terms(std::forward(args)...); - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); + assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) -template -product_operator::product_operator(scalar_operator coefficient, const std::vector &atomic_operators, int size) - : coefficient(std::move(coefficient)) { - if (size <= 0) this->operators = atomic_operators; +template +product_operator::product_operator( + scalar_operator coefficient, const std::vector &atomic_operators, + int size) + : coefficient(std::move(coefficient)) { + if (size <= 0) + this->operators = atomic_operators; else { this->operators.reserve(size); for (const auto &op : atomic_operators) this->operators.push_back(op); } - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); + assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } // assumes canonical ordering (if possible) -template -product_operator::product_operator(scalar_operator coefficient, std::vector &&atomic_operators, int size) - : coefficient(std::move(coefficient)), operators(std::move(atomic_operators)) { - if (size > 0) this->operators.reserve(size); - assert (!HandlerTy::can_be_canonicalized || this->is_canonicalized()); +template +product_operator::product_operator( + scalar_operator coefficient, std::vector &&atomic_operators, + int size) + : coefficient(std::move(coefficient)), + operators(std::move(atomic_operators)) { + if (size > 0) + this->operators.reserve(size); + assert(!HandlerTy::can_be_canonicalized || this->is_canonicalized()); } -template -template::value && std::is_constructible::value, bool>> -product_operator::product_operator(const product_operator &other) - : coefficient(other.coefficient) { +template +template ::value && + std::is_constructible::value, + bool>> +product_operator::product_operator(const product_operator &other) + : coefficient(other.coefficient) { this->operators.reserve(other.operators.size()); for (const T &other_op : other.operators) { HandlerTy op(other_op); @@ -292,10 +327,12 @@ product_operator::product_operator(const product_operator &other) } } -template -product_operator::product_operator(const product_operator &other, int size) - : coefficient(other.coefficient) { - if (size <= 0) this->operators = other.operators; +template +product_operator::product_operator( + const product_operator &other, int size) + : coefficient(other.coefficient) { + if (size <= 0) + this->operators = other.operators; else { this->operators.reserve(size); for (const auto &op : other.operators) @@ -303,53 +340,47 @@ product_operator::product_operator(const product_operator } } -template -product_operator::product_operator(product_operator &&other, int size) - : coefficient(std::move(other.coefficient)), operators(std::move(other.operators)) { - if (size > 0) this->operators.reserve(size); -} - -#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ - \ - template \ - product_operator::product_operator(double coefficient); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient); \ - \ - template \ - product_operator::product_operator(HandlerTy &&atomic); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - HandlerTy &&atomic1); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - HandlerTy &&atomic1, \ - HandlerTy &&atomic2); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - HandlerTy &&atomic1, \ - HandlerTy &&atomic2, \ - HandlerTy &&atomic3); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - const std::vector &atomic_operators, int size); \ - \ - template \ - product_operator::product_operator(scalar_operator coefficient, \ - std::vector &&atomic_operators, int size); \ - \ - template \ - product_operator::product_operator( \ - const product_operator &other, int size); \ - \ - template \ - product_operator::product_operator( \ - product_operator &&other, int size); +template +product_operator::product_operator( + product_operator &&other, int size) + : coefficient(std::move(other.coefficient)), + operators(std::move(other.operators)) { + if (size > 0) + this->operators.reserve(size); +} + +#define INSTANTIATE_PRODUCT_CONSTRUCTORS(HandlerTy) \ + \ + template product_operator::product_operator(double coefficient); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient); \ + \ + template product_operator::product_operator(HandlerTy &&atomic); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, HandlerTy &&atomic1); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, HandlerTy &&atomic1, HandlerTy &&atomic2); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, HandlerTy &&atomic1, HandlerTy &&atomic2, \ + HandlerTy &&atomic3); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, \ + const std::vector &atomic_operators, int size); \ + \ + template product_operator::product_operator( \ + scalar_operator coefficient, std::vector &&atomic_operators, \ + int size); \ + \ + template product_operator::product_operator( \ + const product_operator &other, int size); \ + \ + template product_operator::product_operator( \ + product_operator &&other, int size); template product_operator::product_operator( const product_operator &other); @@ -423,10 +454,13 @@ std::string product_operator::to_string() const { return std::move(str); } -template -matrix_2 product_operator::to_matrix(std::unordered_map dimensions, - const std::unordered_map> ¶meters) const { - return std::move(this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix()); +template +matrix_2 product_operator::to_matrix( + std::unordered_map dimensions, + const std::unordered_map> ¶meters) + const { + return std::move( + this->m_evaluate(MatrixArithmetics(dimensions, parameters)).matrix()); } #define INSTANTIATE_PRODUCT_EVALUATIONS(HandlerTy) \ @@ -444,9 +478,11 @@ INSTANTIATE_PRODUCT_EVALUATIONS(boson_operator); // comparisons -template -bool product_operator::operator==(const product_operator &other) const { - return this->coefficient == other.coefficient && this->get_term_id() == other.get_term_id(); +template +bool product_operator::operator==( + const product_operator &other) const { + return this->coefficient == other.coefficient && + this->get_term_id() == other.get_term_id(); } #define INSTANTIATE_PRODUCT_COMPARISONS(HandlerTy) \ @@ -481,15 +517,15 @@ product_operator product_operator::operator+() && { return std::move(*this); } -#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ - template \ - product_operator product_operator::operator-() const &; \ - template \ - product_operator product_operator::operator-() &&; \ - template \ - product_operator product_operator::operator+() const &; \ - template \ - product_operator product_operator::operator+() &&; +#define INSTANTIATE_PRODUCT_UNARY_OPS(HandlerTy) \ + template product_operator \ + product_operator::operator-() const &; \ + template product_operator \ + product_operator::operator-() &&; \ + template product_operator \ + product_operator::operator+() const &; \ + template product_operator \ + product_operator::operator+() &&; INSTANTIATE_PRODUCT_UNARY_OPS(matrix_operator); INSTANTIATE_PRODUCT_UNARY_OPS(spin_operator); @@ -497,41 +533,41 @@ INSTANTIATE_PRODUCT_UNARY_OPS(boson_operator); // right-hand arithmetics -#define PRODUCT_MULTIPLICATION(otherTy) \ - \ - template \ - product_operator product_operator::operator*( \ - otherTy other) const & { \ - return product_operator(other * this->coefficient, \ - this->operators); \ - } \ - \ - template \ - product_operator product_operator::operator*( \ - otherTy other) && { \ - this->coefficient *= other; \ - return std::move(*this); \ - } \ +#define PRODUCT_MULTIPLICATION(otherTy) \ + \ + template \ + product_operator product_operator::operator*( \ + otherTy other) const & { \ + return product_operator(other * this->coefficient, \ + this->operators); \ + } \ + \ + template \ + product_operator product_operator::operator*( \ + otherTy other) && { \ + this->coefficient *= other; \ + return std::move(*this); \ + } PRODUCT_MULTIPLICATION(double); PRODUCT_MULTIPLICATION(std::complex); PRODUCT_MULTIPLICATION(const scalar_operator &); -#define PRODUCT_ADDITION(otherTy, op) \ - \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) const & { \ - return operator_sum(product_operator(op other), \ - product_operator(*this)); \ - } \ - \ - template \ - operator_sum product_operator::operator op( \ - otherTy other) && { \ - return operator_sum(product_operator(op other), \ - std::move(*this)); \ - } \ +#define PRODUCT_ADDITION(otherTy, op) \ + \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) const & { \ + return operator_sum(product_operator(op other), \ + product_operator(*this)); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + otherTy other) && { \ + return operator_sum(product_operator(op other), \ + std::move(*this)); \ + } PRODUCT_ADDITION(double, +); PRODUCT_ADDITION(double, -); @@ -540,60 +576,63 @@ PRODUCT_ADDITION(std::complex, -); PRODUCT_ADDITION(const scalar_operator &, +); PRODUCT_ADDITION(const scalar_operator &, -); -#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ - \ - template \ - product_operator product_operator::operator*(double other) const &; \ - template \ - product_operator product_operator::operator*(double other) &&; \ - template \ - operator_sum product_operator::operator+(double other) const &; \ - template \ - operator_sum product_operator::operator+(double other) &&; \ - template \ - operator_sum product_operator::operator-(double other) const &; \ - template \ - operator_sum product_operator::operator-(double other) &&; \ - template \ - product_operator product_operator::operator*(std::complex other) const &; \ - template \ - product_operator product_operator::operator*(std::complex other) &&; \ - template \ - operator_sum product_operator::operator+(std::complex other) const &; \ - template \ - operator_sum product_operator::operator+(std::complex other) &&; \ - template \ - operator_sum product_operator::operator-(std::complex other) const &; \ - template \ - operator_sum product_operator::operator-(std::complex other) &&; \ - template \ - product_operator product_operator::operator*(const scalar_operator &other) const &; \ - template \ - product_operator product_operator::operator*(const scalar_operator &other) &&; \ - template \ - operator_sum product_operator::operator+(const scalar_operator &other) const &; \ - template \ - operator_sum product_operator::operator+(const scalar_operator &other) &&; \ - template \ - operator_sum product_operator::operator-(const scalar_operator &other) const &; \ - template \ - operator_sum product_operator::operator-(const scalar_operator &other) &&; \ +#define INSTANTIATE_PRODUCT_RHSIMPLE_OPS(HandlerTy) \ + \ + template product_operator product_operator::operator*( \ + double other) const &; \ + template product_operator product_operator::operator*( \ + double other) &&; \ + template operator_sum product_operator::operator+( \ + double other) const &; \ + template operator_sum product_operator::operator+( \ + double other) &&; \ + template operator_sum product_operator::operator-( \ + double other) const &; \ + template operator_sum product_operator::operator-( \ + double other) &&; \ + template product_operator product_operator::operator*( \ + std::complex other) const &; \ + template product_operator product_operator::operator*( \ + std::complex other) &&; \ + template operator_sum product_operator::operator+( \ + std::complex other) const &; \ + template operator_sum product_operator::operator+( \ + std::complex other) &&; \ + template operator_sum product_operator::operator-( \ + std::complex other) const &; \ + template operator_sum product_operator::operator-( \ + std::complex other) &&; \ + template product_operator product_operator::operator*( \ + const scalar_operator &other) const &; \ + template product_operator product_operator::operator*( \ + const scalar_operator &other) &&; \ + template operator_sum product_operator::operator+( \ + const scalar_operator &other) const &; \ + template operator_sum product_operator::operator+( \ + const scalar_operator &other) &&; \ + template operator_sum product_operator::operator-( \ + const scalar_operator &other) const &; \ + template operator_sum product_operator::operator-( \ + const scalar_operator &other) &&; INSTANTIATE_PRODUCT_RHSIMPLE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(spin_operator); INSTANTIATE_PRODUCT_RHSIMPLE_OPS(boson_operator); template -product_operator product_operator::operator*(const product_operator &other) const & { - product_operator prod(this->coefficient * other.coefficient, this->operators, - this->operators.size() + other.operators.size()); +product_operator product_operator::operator*( + const product_operator &other) const & { + product_operator prod( + this->coefficient * other.coefficient, this->operators, + this->operators.size() + other.operators.size()); for (HandlerTy op : other.operators) prod.insert(std::move(op)); return std::move(prod); } template -product_operator product_operator::operator*(const product_operator &other) && { +product_operator product_operator::operator*( + const product_operator &other) && { this->coefficient *= other.coefficient; this->operators.reserve(this->operators.size() + other.operators.size()); for (HandlerTy op : other.operators) @@ -602,16 +641,19 @@ product_operator product_operator::operator*(const product } template -product_operator product_operator::operator*(product_operator &&other) const & { - product_operator prod(this->coefficient * std::move(other.coefficient), - this->operators, this->operators.size() + other.operators.size()); +product_operator product_operator::operator*( + product_operator &&other) const & { + product_operator prod( + this->coefficient * std::move(other.coefficient), this->operators, + this->operators.size() + other.operators.size()); for (auto &&op : other.operators) prod.insert(std::move(op)); return std::move(prod); } template -product_operator product_operator::operator*(product_operator &&other) && { +product_operator +product_operator::operator*(product_operator &&other) && { this->coefficient *= std::move(other.coefficient); this->operators.reserve(this->operators.size() + other.operators.size()); for (auto &&op : other.operators) @@ -619,154 +661,139 @@ product_operator product_operator::operator*(product_opera return std::move(*this); } -#define PRODUCT_ADDITION_PRODUCT(op) \ - \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) const & { \ - return operator_sum(product_operator(*this), op other); \ - } \ - \ - template \ - operator_sum product_operator::operator op( \ - const product_operator &other) && { \ - return operator_sum(std::move(*this), op other); \ - } \ - \ - template \ - operator_sum product_operator::operator op( \ - product_operator &&other) const & { \ - return operator_sum(product_operator(*this), \ - op std::move(other)); \ - } \ - \ - template \ - operator_sum product_operator::operator op( \ - product_operator &&other) && { \ - return operator_sum(std::move(*this), op std::move(other)); \ - } \ +#define PRODUCT_ADDITION_PRODUCT(op) \ + \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) const & { \ + return operator_sum(product_operator(*this), \ + op other); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + const product_operator &other) && { \ + return operator_sum(std::move(*this), op other); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + product_operator &&other) const & { \ + return operator_sum(product_operator(*this), \ + op std::move(other)); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + product_operator &&other) && { \ + return operator_sum(std::move(*this), op std::move(other)); \ + } PRODUCT_ADDITION_PRODUCT(+) PRODUCT_ADDITION_PRODUCT(-) template -operator_sum product_operator::operator*(const operator_sum &other) const { - operator_sum sum; // everything needs to be updated, so creating a new sum makes sense +operator_sum product_operator::operator*( + const operator_sum &other) const { + operator_sum + sum; // everything needs to be updated, so creating a new sum makes sense sum.coefficients.reserve(other.coefficients.size()); sum.term_map.reserve(other.terms.size()); sum.terms.reserve(other.terms.size()); for (auto i = 0; i < other.terms.size(); ++i) { - auto prod = *this * product_operator(other.coefficients[i], other.terms[i]); + auto prod = *this * product_operator(other.coefficients[i], + other.terms[i]); sum.insert(std::move(prod)); } return std::move(sum); } -#define PRODUCT_ADDITION_SUM(op) \ - \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) const & { \ - operator_sum sum; \ - sum.coefficients.reserve(other.coefficients.size() + 1); \ - sum.term_map = other.term_map; \ - sum.terms = other.terms; \ - for (auto &coeff : other.coefficients) \ - sum.coefficients.push_back(op coeff); \ - sum.insert(*this); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum product_operator::operator op( \ - const operator_sum &other) && { \ - operator_sum sum; \ - sum.coefficients.reserve(other.coefficients.size() + 1); \ - sum.term_map = other.term_map; \ - sum.terms = other.terms; \ - for (auto &coeff : other.coefficients) \ - sum.coefficients.push_back(op coeff); \ - sum.insert(std::move(*this)); \ - return std::move(sum); \ - } \ - template \ - operator_sum product_operator::operator op( \ - operator_sum &&other) const & { \ - operator_sum sum(op std::move(other)); \ - sum.insert(*this); \ - return std::move(sum); \ - } \ - \ - template \ - operator_sum product_operator::operator op( \ - operator_sum &&other) && { \ - operator_sum sum(op std::move(other)); \ - sum.insert(std::move(*this)); \ - return std::move(sum); \ - } \ +#define PRODUCT_ADDITION_SUM(op) \ + \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) const & { \ + operator_sum sum; \ + sum.coefficients.reserve(other.coefficients.size() + 1); \ + sum.term_map = other.term_map; \ + sum.terms = other.terms; \ + for (auto &coeff : other.coefficients) \ + sum.coefficients.push_back(op coeff); \ + sum.insert(*this); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + const operator_sum &other) && { \ + operator_sum sum; \ + sum.coefficients.reserve(other.coefficients.size() + 1); \ + sum.term_map = other.term_map; \ + sum.terms = other.terms; \ + for (auto &coeff : other.coefficients) \ + sum.coefficients.push_back(op coeff); \ + sum.insert(std::move(*this)); \ + return std::move(sum); \ + } \ + template \ + operator_sum product_operator::operator op( \ + operator_sum &&other) const & { \ + operator_sum sum(op std::move(other)); \ + sum.insert(*this); \ + return std::move(sum); \ + } \ + \ + template \ + operator_sum product_operator::operator op( \ + operator_sum &&other) && { \ + operator_sum sum(op std::move(other)); \ + sum.insert(std::move(*this)); \ + return std::move(sum); \ + } PRODUCT_ADDITION_SUM(+) PRODUCT_ADDITION_SUM(-) -#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - product_operator product_operator::operator*( \ - const product_operator &other) const &; \ - template \ - product_operator product_operator::operator*( \ - const product_operator &other) &&; \ - template \ - operator_sum product_operator::operator+( \ - const product_operator &other) const &; \ - template \ - operator_sum product_operator::operator+( \ - const product_operator &other) &&; \ - template \ - operator_sum product_operator::operator+( \ - product_operator &&other) const &; \ - template \ - operator_sum product_operator::operator+( \ - product_operator &&other) &&; \ - template \ - operator_sum product_operator::operator-( \ - const product_operator &other) const &; \ - template \ - operator_sum product_operator::operator-( \ - const product_operator &other) &&; \ - template \ - operator_sum product_operator::operator-( \ - product_operator &&other) const &; \ - template \ - operator_sum product_operator::operator-( \ - product_operator &&other) &&; \ - template \ - operator_sum product_operator::operator*( \ - const operator_sum &other) const; \ - template \ - operator_sum product_operator::operator+( \ - const operator_sum &other) const &; \ - template \ - operator_sum product_operator::operator+( \ - const operator_sum &other) &&; \ - template \ - operator_sum product_operator::operator+( \ - operator_sum &&other) const &; \ - template \ - operator_sum product_operator::operator+( \ - operator_sum &&other) &&; \ - template \ - operator_sum product_operator::operator-( \ - const operator_sum &other) const &; \ - template \ - operator_sum product_operator::operator-( \ - const operator_sum &other) &&; \ - template \ - operator_sum product_operator::operator-( \ - operator_sum &&other) const &; \ - template \ - operator_sum product_operator::operator-( \ - operator_sum &&other) &&; \ +#define INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(HandlerTy) \ + \ + template product_operator product_operator::operator*( \ + const product_operator &other) const &; \ + template product_operator product_operator::operator*( \ + const product_operator &other) &&; \ + template operator_sum product_operator::operator+( \ + const product_operator &other) const &; \ + template operator_sum product_operator::operator+( \ + const product_operator &other) &&; \ + template operator_sum product_operator::operator+( \ + product_operator &&other) const &; \ + template operator_sum product_operator::operator+( \ + product_operator &&other) &&; \ + template operator_sum product_operator::operator-( \ + const product_operator &other) const &; \ + template operator_sum product_operator::operator-( \ + const product_operator &other) &&; \ + template operator_sum product_operator::operator-( \ + product_operator &&other) const &; \ + template operator_sum product_operator::operator-( \ + product_operator &&other) &&; \ + template operator_sum product_operator::operator*( \ + const operator_sum &other) const; \ + template operator_sum product_operator::operator+( \ + const operator_sum &other) const &; \ + template operator_sum product_operator::operator+( \ + const operator_sum &other) &&; \ + template operator_sum product_operator::operator+( \ + operator_sum &&other) const &; \ + template operator_sum product_operator::operator+( \ + operator_sum &&other) &&; \ + template operator_sum product_operator::operator-( \ + const operator_sum &other) const &; \ + template operator_sum product_operator::operator-( \ + const operator_sum &other) &&; \ + template operator_sum product_operator::operator-( \ + operator_sum &&other) const &; \ + template operator_sum product_operator::operator-( \ + operator_sum &&other) &&; INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_RHCOMPOSITE_OPS(spin_operator); @@ -795,7 +822,8 @@ product_operator &product_operator::operator*=( } template -product_operator& product_operator::operator*=(product_operator &&other) { +product_operator & +product_operator::operator*=(product_operator &&other) { this->coefficient *= std::move(other.coefficient); this->operators.reserve(this->operators.size() + other.operators.size()); for (auto &&op : other.operators) @@ -803,18 +831,20 @@ product_operator& product_operator::operator*=(product_ope return *this; } -#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ - \ - template \ - product_operator& product_operator::operator*=(double other); \ - template \ - product_operator& product_operator::operator*=(std::complex other); \ - template \ - product_operator& product_operator::operator*=(const scalar_operator &other); \ - template \ - product_operator& product_operator::operator*=(const product_operator &other); \ - template \ - product_operator& product_operator::operator*=(product_operator &&other); \ +#define INSTANTIATE_PRODUCT_OPASSIGNMENTS(HandlerTy) \ + \ + template product_operator & \ + product_operator::operator*=(double other); \ + template product_operator & \ + product_operator::operator*=(std::complex other); \ + template product_operator & \ + product_operator::operator*=(const scalar_operator &other); \ + template product_operator & \ + product_operator::operator*=( \ + const product_operator &other); \ + template product_operator & \ + product_operator::operator*=( \ + product_operator &&other); INSTANTIATE_PRODUCT_OPASSIGNMENTS(matrix_operator); INSTANTIATE_PRODUCT_OPASSIGNMENTS(spin_operator); @@ -822,40 +852,41 @@ INSTANTIATE_PRODUCT_OPASSIGNMENTS(boson_operator); // left-hand arithmetics -#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ - \ - template \ - product_operator operator*(otherTy other, \ - const product_operator &self) { \ - return product_operator(other * self.coefficient, \ - self.operators); \ - } \ - \ - template \ - product_operator operator*(otherTy other, \ - product_operator &&self) { \ - self.coefficient *= other; \ - return std::move(self); \ - } \ +#define PRODUCT_MULTIPLICATION_REVERSE(otherTy) \ + \ + template \ + product_operator operator*( \ + otherTy other, const product_operator &self) { \ + return product_operator(other * self.coefficient, \ + self.operators); \ + } \ + \ + template \ + product_operator operator*(otherTy other, \ + product_operator &&self) { \ + self.coefficient *= other; \ + return std::move(self); \ + } PRODUCT_MULTIPLICATION_REVERSE(double); PRODUCT_MULTIPLICATION_REVERSE(std::complex); PRODUCT_MULTIPLICATION_REVERSE(const scalar_operator &); -#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ - \ - template \ - operator_sum operator op(otherTy other, \ - const product_operator &self) { \ - return operator_sum(product_operator(other), op self); \ - } \ - \ - template \ - operator_sum operator op(otherTy other, \ - product_operator &&self) { \ - return operator_sum(product_operator(other), \ - op std::move(self)); \ - } \ +#define PRODUCT_ADDITION_REVERSE(otherTy, op) \ + \ + template \ + operator_sum operator op( \ + otherTy other, const product_operator &self) { \ + return operator_sum(product_operator(other), \ + op self); \ + } \ + \ + template \ + operator_sum operator op(otherTy other, \ + product_operator &&self) { \ + return operator_sum(product_operator(other), \ + op std::move(self)); \ + } PRODUCT_ADDITION_REVERSE(double, +); PRODUCT_ADDITION_REVERSE(double, -); @@ -864,44 +895,44 @@ PRODUCT_ADDITION_REVERSE(std::complex, -); PRODUCT_ADDITION_REVERSE(const scalar_operator &, +); PRODUCT_ADDITION_REVERSE(const scalar_operator &, -); -#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ - \ - template \ - product_operator operator*(double other, const product_operator &self); \ - template \ - product_operator operator*(double other, product_operator &&self); \ - template \ - operator_sum operator+(double other, const product_operator &self); \ - template \ - operator_sum operator+(double other, product_operator &&self); \ - template \ - operator_sum operator-(double other, const product_operator &self); \ - template \ - operator_sum operator-(double other, product_operator &&self); \ - template \ - product_operator operator*(std::complex other, const product_operator &self); \ - template \ - product_operator operator*(std::complex other, product_operator &&self); \ - template \ - operator_sum operator+(std::complex other, const product_operator &self); \ - template \ - operator_sum operator+(std::complex other, product_operator &&self); \ - template \ - operator_sum operator-(std::complex other, const product_operator &self); \ - template \ - operator_sum operator-(std::complex other, product_operator &&self); \ - template \ - product_operator operator*(const scalar_operator &other, const product_operator &self); \ - template \ - product_operator operator*(const scalar_operator &other, product_operator &&self); \ - template \ - operator_sum operator+(const scalar_operator &other, const product_operator &self); \ - template \ - operator_sum operator+(const scalar_operator &other, product_operator &&self); \ - template \ - operator_sum operator-(const scalar_operator &other, const product_operator &self); \ - template \ - operator_sum operator-(const scalar_operator &other, product_operator &&self); \ +#define INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(HandlerTy) \ + \ + template product_operator operator*( \ + double other, const product_operator &self); \ + template product_operator operator*( \ + double other, product_operator &&self); \ + template operator_sum operator+( \ + double other, const product_operator &self); \ + template operator_sum operator+( \ + double other, product_operator &&self); \ + template operator_sum operator-( \ + double other, const product_operator &self); \ + template operator_sum operator-( \ + double other, product_operator &&self); \ + template product_operator operator*( \ + std::complex other, const product_operator &self); \ + template product_operator operator*( \ + std::complex other, product_operator &&self); \ + template operator_sum operator+( \ + std::complex other, const product_operator &self); \ + template operator_sum operator+( \ + std::complex other, product_operator &&self); \ + template operator_sum operator-( \ + std::complex other, const product_operator &self); \ + template operator_sum operator-( \ + std::complex other, product_operator &&self); \ + template product_operator operator*( \ + const scalar_operator &other, const product_operator &self); \ + template product_operator operator*( \ + const scalar_operator &other, product_operator &&self); \ + template operator_sum operator+( \ + const scalar_operator &other, const product_operator &self); \ + template operator_sum operator+( \ + const scalar_operator &other, product_operator &&self); \ + template operator_sum operator-( \ + const scalar_operator &other, const product_operator &self); \ + template operator_sum operator-( \ + const scalar_operator &other, product_operator &&self); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(matrix_operator); INSTANTIATE_PRODUCT_LHCOMPOSITE_OPS(spin_operator); diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index 28d6be6610..cbeafa19c4 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -75,7 +75,8 @@ matrix_2 scalar_operator::to_matrix( std::string scalar_operator::to_string() const { if (std::holds_alternative>(this->value)) { auto value = std::get>(this->value); - return "(" + std::to_string(value.real()) + "+" + std::to_string(value.imag()) + "i)"; + return "(" + std::to_string(value.real()) + "+" + + std::to_string(value.imag()) + "i)"; } return "scalar"; } diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index b498de3251..99cec8eccd 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -53,12 +53,11 @@ std::vector spin_operator::degrees() const { return {this->target}; } // constructors -spin_operator::spin_operator(int target) - : op_code(0), target(target) {} +spin_operator::spin_operator(int target) : op_code(0), target(target) {} -spin_operator::spin_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); +spin_operator::spin_operator(int target, int op_id) + : op_code(op_id), target(target) { + assert(0 <= op_id < 4); } // evaluations diff --git a/runtime/cudaq/dynamics/spin_operators.h b/runtime/cudaq/dynamics/spin_operators.h index 99bfeed893..7fe850ef7b 100644 --- a/runtime/cudaq/dynamics/spin_operators.h +++ b/runtime/cudaq/dynamics/spin_operators.h @@ -21,8 +21,9 @@ template class product_operator; // FIXME: rename to spin ... -class spin_operator : public operator_handler{ -template friend class product_operator; +class spin_operator : public operator_handler { + template + friend class product_operator; private: // I = 0, Z = 1, X = 2, Y = 3 diff --git a/runtime/cudaq/dynamics/templates.h b/runtime/cudaq/dynamics/templates.h index 632b6693f4..a2a7218dd3 100644 --- a/runtime/cudaq/dynamics/templates.h +++ b/runtime/cudaq/dynamics/templates.h @@ -31,42 +31,60 @@ class operator_sum; std::is_base_of::value, \ bool> -template -product_operator operator*(double other, const product_operator &self); -template -product_operator operator*(double other, product_operator &&self); -template -operator_sum operator+(double other, const product_operator &self); -template -operator_sum operator+(double other, product_operator &&self); -template -operator_sum operator-(double other, const product_operator &self); -template -operator_sum operator-(double other, product_operator &&self); -template -product_operator operator*(std::complex other, const product_operator &self); -template -product_operator operator*(std::complex other, product_operator &&self); -template -operator_sum operator+(std::complex other, const product_operator &self); -template -operator_sum operator+(std::complex other, product_operator &&self); -template -operator_sum operator-(std::complex other, const product_operator &self); -template -operator_sum operator-(std::complex other, product_operator &&self); -template -product_operator operator*(const scalar_operator &other, const product_operator &self); -template -product_operator operator*(const scalar_operator &other, product_operator &&self); -template -operator_sum operator+(const scalar_operator &other, const product_operator &self); -template -operator_sum operator+(const scalar_operator &other, product_operator &&self); -template -operator_sum operator-(const scalar_operator &other, const product_operator &self); -template -operator_sum operator-(const scalar_operator &other, product_operator &&self); +template +product_operator operator*(double other, + const product_operator &self); +template +product_operator operator*(double other, + product_operator &&self); +template +operator_sum operator+(double other, + const product_operator &self); +template +operator_sum operator+(double other, + product_operator &&self); +template +operator_sum operator-(double other, + const product_operator &self); +template +operator_sum operator-(double other, + product_operator &&self); +template +product_operator operator*(std::complex other, + const product_operator &self); +template +product_operator operator*(std::complex other, + product_operator &&self); +template +operator_sum operator+(std::complex other, + const product_operator &self); +template +operator_sum operator+(std::complex other, + product_operator &&self); +template +operator_sum operator-(std::complex other, + const product_operator &self); +template +operator_sum operator-(std::complex other, + product_operator &&self); +template +product_operator operator*(const scalar_operator &other, + const product_operator &self); +template +product_operator operator*(const scalar_operator &other, + product_operator &&self); +template +operator_sum operator+(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator+(const scalar_operator &other, + product_operator &&self); +template +operator_sum operator-(const scalar_operator &other, + const product_operator &self); +template +operator_sum operator-(const scalar_operator &other, + product_operator &&self); template @@ -82,61 +100,94 @@ template operator-(const product_operator &other, const product_operator &self); -template -operator_sum operator*(double other, const operator_sum &self); -template +template +operator_sum operator*(double other, + const operator_sum &self); +template operator_sum operator*(double other, operator_sum &&self); -template -operator_sum operator+(double other, const operator_sum &self); -template +template +operator_sum operator+(double other, + const operator_sum &self); +template operator_sum operator+(double other, operator_sum &&self); -template -operator_sum operator-(double other, const operator_sum &self); -template +template +operator_sum operator-(double other, + const operator_sum &self); +template operator_sum operator-(double other, operator_sum &&self); -template -operator_sum operator*(std::complex other, const operator_sum &self); -template -operator_sum operator*(std::complex other, operator_sum &&self); -template -operator_sum operator+(std::complex other, const operator_sum &self); -template -operator_sum operator+(std::complex other, operator_sum &&self); -template -operator_sum operator-(std::complex other, const operator_sum &self); -template -operator_sum operator-(std::complex other, operator_sum &&self); -template -operator_sum operator*(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator*(const scalar_operator &other, operator_sum &&self); -template -operator_sum operator+(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator+(const scalar_operator &other, operator_sum &&self); -template -operator_sum operator-(const scalar_operator &other, const operator_sum &self); -template -operator_sum operator-(const scalar_operator &other, operator_sum &&self); +template +operator_sum operator*(std::complex other, + const operator_sum &self); +template +operator_sum operator*(std::complex other, + operator_sum &&self); +template +operator_sum operator+(std::complex other, + const operator_sum &self); +template +operator_sum operator+(std::complex other, + operator_sum &&self); +template +operator_sum operator-(std::complex other, + const operator_sum &self); +template +operator_sum operator-(std::complex other, + operator_sum &&self); +template +operator_sum operator*(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator*(const scalar_operator &other, + operator_sum &&self); +template +operator_sum operator+(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator+(const scalar_operator &other, + operator_sum &&self); +template +operator_sum operator-(const scalar_operator &other, + const operator_sum &self); +template +operator_sum operator-(const scalar_operator &other, + operator_sum &&self); -template -operator_sum operator*(const operator_sum &other, const product_operator &self); -template -operator_sum operator+(const operator_sum &other, const product_operator &self); -template -operator_sum operator-(const operator_sum &other, const product_operator &self); -template -operator_sum operator*(const product_operator &other, const operator_sum &self); -template -operator_sum operator+(const product_operator &other, const operator_sum &self); -template -operator_sum operator-(const product_operator &other, const operator_sum &self); -template -operator_sum operator*(const operator_sum &other, const operator_sum &self); -template -operator_sum operator+(const operator_sum &other, const operator_sum &self); -template -operator_sum operator-(const operator_sum &other, const operator_sum &self); +template +operator_sum operator*(const operator_sum &other, + const product_operator &self); +template +operator_sum operator+(const operator_sum &other, + const product_operator &self); +template +operator_sum operator-(const operator_sum &other, + const product_operator &self); +template +operator_sum operator*(const product_operator &other, + const operator_sum &self); +template +operator_sum operator+(const product_operator &other, + const operator_sum &self); +template +operator_sum operator-(const product_operator &other, + const operator_sum &self); +template +operator_sum operator*(const operator_sum &other, + const operator_sum &self); +template +operator_sum operator+(const operator_sum &other, + const operator_sum &self); +template +operator_sum operator-(const operator_sum &other, + const operator_sum &self); template @@ -176,81 +227,81 @@ operator_sum operator-(const operator_sum &other, const operator_sum &self); #ifndef CUDAQ_INSTANTIATE_TEMPLATES -#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ - \ - extern template \ - product_operator operator*(double other, const product_operator &self); \ - extern template \ - product_operator operator*(double other, product_operator &&self); \ - extern template \ - operator_sum operator+(double other, const product_operator &self); \ - extern template \ - operator_sum operator+(double other, product_operator &&self); \ - extern template \ - operator_sum operator-(double other, const product_operator &self); \ - extern template \ - operator_sum operator-(double other, product_operator &&self); \ - extern template \ - product_operator operator*(std::complex other, const product_operator &self); \ - extern template \ - product_operator operator*(std::complex other, product_operator &&self); \ - extern template \ - operator_sum operator+(std::complex other, const product_operator &self); \ - extern template \ - operator_sum operator+(std::complex other, product_operator &&self); \ - extern template \ - operator_sum operator-(std::complex other, const product_operator &self); \ - extern template \ - operator_sum operator-(std::complex other, product_operator &&self); \ - extern template \ - product_operator operator*(const scalar_operator &other, const product_operator &self); \ - extern template \ - product_operator operator*(const scalar_operator &other, product_operator &&self); \ - extern template \ - operator_sum operator+(const scalar_operator &other, const product_operator &self); \ - extern template \ - operator_sum operator+(const scalar_operator &other, product_operator &&self); \ - extern template \ - operator_sum operator-(const scalar_operator &other, const product_operator &self); \ - extern template \ - operator_sum operator-(const scalar_operator &other, product_operator &&self); \ - \ - extern template \ - operator_sum operator*(double other, const operator_sum &self); \ - extern template \ - operator_sum operator*(double other, operator_sum &&self); \ - extern template \ - operator_sum operator+(double other, const operator_sum &self); \ - extern template \ - operator_sum operator+(double other, operator_sum &&self); \ - extern template \ - operator_sum operator-(double other, const operator_sum &self); \ - extern template \ - operator_sum operator-(double other, operator_sum &&self); \ - extern template \ - operator_sum operator*(std::complex other, const operator_sum &self); \ - extern template \ - operator_sum operator*(std::complex other, operator_sum &&self); \ - extern template \ - operator_sum operator+(std::complex other, const operator_sum &self); \ - extern template \ - operator_sum operator+(std::complex other, operator_sum &&self); \ - extern template \ - operator_sum operator-(std::complex other, const operator_sum &self); \ - extern template \ - operator_sum operator-(std::complex other, operator_sum &&self); \ - extern template \ - operator_sum operator*(const scalar_operator &other, const operator_sum &self); \ - extern template \ - operator_sum operator*(const scalar_operator &other, operator_sum &&self); \ - extern template \ - operator_sum operator+(const scalar_operator &other, const operator_sum &self); \ - extern template \ - operator_sum operator+(const scalar_operator &other, operator_sum &&self); \ - extern template \ - operator_sum operator-(const scalar_operator &other, const operator_sum &self); \ - extern template \ - operator_sum operator-(const scalar_operator &other, operator_sum &&self); \ +#define EXTERN_TEMPLATE_SPECIALIZATIONS(HandlerTy) \ + \ + extern template product_operator operator*( \ + double other, const product_operator &self); \ + extern template product_operator operator*( \ + double other, product_operator &&self); \ + extern template operator_sum operator+( \ + double other, const product_operator &self); \ + extern template operator_sum operator+( \ + double other, product_operator &&self); \ + extern template operator_sum operator-( \ + double other, const product_operator &self); \ + extern template operator_sum operator-( \ + double other, product_operator &&self); \ + extern template product_operator operator*( \ + std::complex other, const product_operator &self); \ + extern template product_operator operator*( \ + std::complex other, product_operator &&self); \ + extern template operator_sum operator+( \ + std::complex other, const product_operator &self); \ + extern template operator_sum operator+( \ + std::complex other, product_operator &&self); \ + extern template operator_sum operator-( \ + std::complex other, const product_operator &self); \ + extern template operator_sum operator-( \ + std::complex other, product_operator &&self); \ + extern template product_operator operator*( \ + const scalar_operator &other, const product_operator &self); \ + extern template product_operator operator*( \ + const scalar_operator &other, product_operator &&self); \ + extern template operator_sum operator+( \ + const scalar_operator &other, const product_operator &self); \ + extern template operator_sum operator+( \ + const scalar_operator &other, product_operator &&self); \ + extern template operator_sum operator-( \ + const scalar_operator &other, const product_operator &self); \ + extern template operator_sum operator-( \ + const scalar_operator &other, product_operator &&self); \ + \ + extern template operator_sum operator*( \ + double other, const operator_sum &self); \ + extern template operator_sum operator*( \ + double other, operator_sum &&self); \ + extern template operator_sum operator+( \ + double other, const operator_sum &self); \ + extern template operator_sum operator+( \ + double other, operator_sum &&self); \ + extern template operator_sum operator-( \ + double other, const operator_sum &self); \ + extern template operator_sum operator-( \ + double other, operator_sum &&self); \ + extern template operator_sum operator*( \ + std::complex other, const operator_sum &self); \ + extern template operator_sum operator*( \ + std::complex other, operator_sum &&self); \ + extern template operator_sum operator+( \ + std::complex other, const operator_sum &self); \ + extern template operator_sum operator+( \ + std::complex other, operator_sum &&self); \ + extern template operator_sum operator-( \ + std::complex other, const operator_sum &self); \ + extern template operator_sum operator-( \ + std::complex other, operator_sum &&self); \ + extern template operator_sum operator*( \ + const scalar_operator &other, const operator_sum &self); \ + extern template operator_sum operator*( \ + const scalar_operator &other, operator_sum &&self); \ + extern template operator_sum operator+( \ + const scalar_operator &other, const operator_sum &self); \ + extern template operator_sum operator+( \ + const scalar_operator &other, operator_sum &&self); \ + extern template operator_sum operator-( \ + const scalar_operator &other, const operator_sum &self); \ + extern template operator_sum operator-( \ + const scalar_operator &other, operator_sum &&self); EXTERN_TEMPLATE_SPECIALIZATIONS(matrix_operator); EXTERN_TEMPLATE_SPECIALIZATIONS(spin_operator); diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index b6b5e8bd51..09694ea79c 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -8,11 +8,10 @@ #pragma once -#include -#include #include +#include +#include -#include "utils/tensor.h" #include "dynamics/operator_leafs.h" #include "dynamics/templates.h" #include "utils/tensor.h" @@ -47,8 +46,9 @@ class operator_sum { bool pad_terms = true) const; protected: - - std::unordered_map term_map; // quick access to term index given its id (used for aggregating terms) + std::unordered_map + term_map; // quick access to term index given its id (used for aggregating + // terms) std::vector> terms; std::vector coefficients; @@ -156,44 +156,53 @@ class operator_sum { operator_sum operator+(const scalar_operator &other) &&; operator_sum operator-(const scalar_operator &other) const &; operator_sum operator-(const scalar_operator &other) &&; - operator_sum operator*(const product_operator &other) const; - operator_sum operator+(const product_operator &other) const &; - operator_sum operator+(const product_operator &other) &&; - operator_sum operator+(product_operator &&other) const &; + operator_sum + operator*(const product_operator &other) const; + operator_sum + operator+(const product_operator &other) const &; + operator_sum + operator+(const product_operator &other) &&; + operator_sum + operator+(product_operator &&other) const &; operator_sum operator+(product_operator &&other) &&; - operator_sum operator-(const product_operator &other) const &; - operator_sum operator-(const product_operator &other) &&; - operator_sum operator-(product_operator &&other) const &; + operator_sum + operator-(const product_operator &other) const &; + operator_sum + operator-(const product_operator &other) &&; + operator_sum + operator-(product_operator &&other) const &; operator_sum operator-(product_operator &&other) &&; operator_sum operator*(const operator_sum &other) const; - operator_sum operator+(const operator_sum &other) const &; + operator_sum + operator+(const operator_sum &other) const &; operator_sum operator+(const operator_sum &other) &&; operator_sum operator+(operator_sum &&other) const &; operator_sum operator+(operator_sum &&other) &&; - operator_sum operator-(const operator_sum &other) const &; + operator_sum + operator-(const operator_sum &other) const &; operator_sum operator-(const operator_sum &other) &&; operator_sum operator-(operator_sum &&other) const &; operator_sum operator-(operator_sum &&other) &&; - operator_sum& operator*=(double other); - operator_sum& operator+=(double other); - operator_sum& operator-=(double other); - operator_sum& operator*=(std::complex other); - operator_sum& operator+=(std::complex other); - operator_sum& operator-=(std::complex other); - operator_sum& operator*=(const scalar_operator &other); - operator_sum& operator+=(const scalar_operator &other); - operator_sum& operator-=(const scalar_operator &other); - operator_sum& operator*=(const product_operator &other); - operator_sum& operator+=(const product_operator &other); - operator_sum& operator+=(product_operator &&other); - operator_sum& operator-=(const product_operator &other); - operator_sum& operator-=(product_operator &&other); - operator_sum& operator*=(const operator_sum &other); - operator_sum& operator+=(const operator_sum &other); - operator_sum& operator+=(operator_sum &&other); - operator_sum& operator-=(const operator_sum &other); - operator_sum& operator-=(operator_sum &&other); + operator_sum &operator*=(double other); + operator_sum &operator+=(double other); + operator_sum &operator-=(double other); + operator_sum &operator*=(std::complex other); + operator_sum &operator+=(std::complex other); + operator_sum &operator-=(std::complex other); + operator_sum &operator*=(const scalar_operator &other); + operator_sum &operator+=(const scalar_operator &other); + operator_sum &operator-=(const scalar_operator &other); + operator_sum &operator*=(const product_operator &other); + operator_sum &operator+=(const product_operator &other); + operator_sum &operator+=(product_operator &&other); + operator_sum &operator-=(const product_operator &other); + operator_sum &operator-=(product_operator &&other); + operator_sum &operator*=(const operator_sum &other); + operator_sum &operator+=(const operator_sum &other); + operator_sum &operator+=(operator_sum &&other); + operator_sum &operator-=(const operator_sum &other); + operator_sum &operator-=(operator_sum &&other); // left-hand arithmetics @@ -201,65 +210,87 @@ class operator_sum { // instantiation is a nightmare. template friend operator_sum operator*(double other, const operator_sum &self); - template + template friend operator_sum operator*(double other, operator_sum &&self); - template + template friend operator_sum operator+(double other, const operator_sum &self); - template + template friend operator_sum operator+(double other, operator_sum &&self); - template + template friend operator_sum operator-(double other, const operator_sum &self); - template + template friend operator_sum operator-(double other, operator_sum &&self); - template - friend operator_sum operator*(std::complex other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, operator_sum &&self); - template - friend operator_sum operator+(std::complex other, const operator_sum &self); - template - friend operator_sum operator+(std::complex other, operator_sum &&self); - template - friend operator_sum operator-(std::complex other, const operator_sum &self); - template - friend operator_sum operator-(std::complex other, operator_sum &&self); - template - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, operator_sum &&self); - template - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, operator_sum &&self); - template - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, operator_sum &&self); - - template - friend operator_sum operator+(double other, const product_operator &self); - template + template + friend operator_sum operator*(std::complex other, + const operator_sum &self); + template + friend operator_sum operator*(std::complex other, + operator_sum &&self); + template + friend operator_sum operator+(std::complex other, + const operator_sum &self); + template + friend operator_sum operator+(std::complex other, + operator_sum &&self); + template + friend operator_sum operator-(std::complex other, + const operator_sum &self); + template + friend operator_sum operator-(std::complex other, + operator_sum &&self); + template + friend operator_sum operator*(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, + operator_sum &&self); + template + friend operator_sum operator+(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, + operator_sum &&self); + template + friend operator_sum operator-(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, + operator_sum &&self); + + template + friend operator_sum operator+(double other, + const product_operator &self); + template friend operator_sum operator+(double other, product_operator &&self); - template - friend operator_sum operator-(double other, const product_operator &self); - template + template + friend operator_sum operator-(double other, + const product_operator &self); + template friend operator_sum operator-(double other, product_operator &&self); - template - friend operator_sum operator+(std::complex other, const product_operator &self); - template - friend operator_sum operator+(std::complex other, product_operator &&self); - template - friend operator_sum operator-(std::complex other, const product_operator &self); - template - friend operator_sum operator-(std::complex other, product_operator &&self); - template - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, product_operator &&self); - template - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, product_operator &&self); + template + friend operator_sum operator+(std::complex other, + const product_operator &self); + template + friend operator_sum operator+(std::complex other, + product_operator &&self); + template + friend operator_sum operator-(std::complex other, + const product_operator &self); + template + friend operator_sum operator-(std::complex other, + product_operator &&self); + template + friend operator_sum operator+(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, + product_operator &&self); + template + friend operator_sum operator-(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, + product_operator &&self); // common operators @@ -295,13 +326,20 @@ class product_operator { #if !defined(NDEBUG) bool is_canonicalized() const; #endif - - typename std::vector::const_iterator find_insert_at(const HandlerTy &other) const; - template::value && !product_operator::supports_inplace_mult, int> = 0> + typename std::vector::const_iterator + find_insert_at(const HandlerTy &other) const; + + template ::value && + !product_operator::supports_inplace_mult, + int> = 0> void insert(T &&other); - template ::value && product_operator::supports_inplace_mult, bool> = true> + template ::value && + product_operator::supports_inplace_mult, + bool> = true> void insert(T &&other); std::string get_term_id() const; @@ -324,11 +362,16 @@ class product_operator { bool> = true> product_operator(scalar_operator coefficient, Args &&...args); - // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, const std::vector &atomic_operators, int size = 0); + // keep this constructor protected (otherwise it needs to ensure canonical + // order) + product_operator(scalar_operator coefficient, + const std::vector &atomic_operators, + int size = 0); - // keep this constructor protected (otherwise it needs to ensure canonical order) - product_operator(scalar_operator coefficient, std::vector &&atomic_operators, int size = 0); + // keep this constructor protected (otherwise it needs to ensure canonical + // order) + product_operator(scalar_operator coefficient, + std::vector &&atomic_operators, int size = 0); public: // read-only properties @@ -438,110 +481,151 @@ class product_operator { operator_sum operator+(const scalar_operator &other) &&; operator_sum operator-(const scalar_operator &other) const &; operator_sum operator-(const scalar_operator &other) &&; - product_operator operator*(const product_operator &other) const &; - product_operator operator*(const product_operator &other) &&; - product_operator operator*(product_operator &&other) const &; + product_operator + operator*(const product_operator &other) const &; + product_operator + operator*(const product_operator &other) &&; + product_operator + operator*(product_operator &&other) const &; product_operator operator*(product_operator &&other) &&; - operator_sum operator+(const product_operator &other) const &; - operator_sum operator+(const product_operator &other) &&; - operator_sum operator+(product_operator &&other) const &; + operator_sum + operator+(const product_operator &other) const &; + operator_sum + operator+(const product_operator &other) &&; + operator_sum + operator+(product_operator &&other) const &; operator_sum operator+(product_operator &&other) &&; - operator_sum operator-(const product_operator &other) const &; - operator_sum operator-(const product_operator &other) &&; - operator_sum operator-(product_operator &&other) const &; + operator_sum + operator-(const product_operator &other) const &; + operator_sum + operator-(const product_operator &other) &&; + operator_sum + operator-(product_operator &&other) const &; operator_sum operator-(product_operator &&other) &&; operator_sum operator*(const operator_sum &other) const; - operator_sum operator+(const operator_sum &other) const &; + operator_sum + operator+(const operator_sum &other) const &; operator_sum operator+(const operator_sum &other) &&; operator_sum operator+(operator_sum &&other) const &; operator_sum operator+(operator_sum &&other) &&; - operator_sum operator-(const operator_sum &other) const &; + operator_sum + operator-(const operator_sum &other) const &; operator_sum operator-(const operator_sum &other) &&; operator_sum operator-(operator_sum &&other) const &; operator_sum operator-(operator_sum &&other) &&; - product_operator& operator*=(double other); - product_operator& operator*=(std::complex other); - product_operator& operator*=(const scalar_operator &other); - product_operator& operator*=(const product_operator &other); - product_operator& operator*=(product_operator &&other); + product_operator &operator*=(double other); + product_operator &operator*=(std::complex other); + product_operator &operator*=(const scalar_operator &other); + product_operator & + operator*=(const product_operator &other); + product_operator &operator*=(product_operator &&other); // left-hand arithmetics - // Being a bit permissive here, since otherwise the explicit template instantiation is a nightmare. - template - friend product_operator operator*(double other, const product_operator &self); - template - friend product_operator operator*(double other, product_operator &&self); - template - friend operator_sum operator+(double other, const product_operator &self); - template + // Being a bit permissive here, since otherwise the explicit template + // instantiation is a nightmare. + template + friend product_operator operator*(double other, + const product_operator &self); + template + friend product_operator operator*(double other, + product_operator &&self); + template + friend operator_sum operator+(double other, + const product_operator &self); + template friend operator_sum operator+(double other, product_operator &&self); - template - friend operator_sum operator-(double other, const product_operator &self); - template + template + friend operator_sum operator-(double other, + const product_operator &self); + template friend operator_sum operator-(double other, product_operator &&self); - template - friend product_operator operator*(std::complex other, const product_operator &self); - template - friend product_operator operator*(std::complex other, product_operator &&self); - template - friend operator_sum operator+(std::complex other, const product_operator &self); - template - friend operator_sum operator+(std::complex other, product_operator &&self); - template - friend operator_sum operator-(std::complex other, const product_operator &self); - template - friend operator_sum operator-(std::complex other, product_operator &&self); - template - friend product_operator operator*(const scalar_operator &other, const product_operator &self); - template - friend product_operator operator*(const scalar_operator &other, product_operator &&self); - template - friend operator_sum operator+(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator+(const scalar_operator &other, product_operator &&self); - template - friend operator_sum operator-(const scalar_operator &other, const product_operator &self); - template - friend operator_sum operator-(const scalar_operator &other, product_operator &&self); - - template + template + friend product_operator operator*(std::complex other, + const product_operator &self); + template + friend product_operator operator*(std::complex other, + product_operator &&self); + template + friend operator_sum operator+(std::complex other, + const product_operator &self); + template + friend operator_sum operator+(std::complex other, + product_operator &&self); + template + friend operator_sum operator-(std::complex other, + const product_operator &self); + template + friend operator_sum operator-(std::complex other, + product_operator &&self); + template + friend product_operator operator*(const scalar_operator &other, + const product_operator &self); + template + friend product_operator operator*(const scalar_operator &other, + product_operator &&self); + template + friend operator_sum operator+(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator+(const scalar_operator &other, + product_operator &&self); + template + friend operator_sum operator-(const scalar_operator &other, + const product_operator &self); + template + friend operator_sum operator-(const scalar_operator &other, + product_operator &&self); + + template friend operator_sum operator*(double other, const operator_sum &self); - template + template friend operator_sum operator*(double other, operator_sum &&self); - template + template friend operator_sum operator+(double other, const operator_sum &self); - template + template friend operator_sum operator+(double other, operator_sum &&self); - template + template friend operator_sum operator-(double other, const operator_sum &self); - template + template friend operator_sum operator-(double other, operator_sum &&self); - template - friend operator_sum operator*(std::complex other, const operator_sum &self); - template - friend operator_sum operator*(std::complex other, operator_sum &&self); - template - friend operator_sum operator+(std::complex other, const operator_sum &self); - template - friend operator_sum operator+(std::complex other, operator_sum &&self); - template - friend operator_sum operator-(std::complex other, const operator_sum &self); - template - friend operator_sum operator-(std::complex other, operator_sum &&self); - template - friend operator_sum operator*(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator*(const scalar_operator &other, operator_sum &&self); - template - friend operator_sum operator+(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator+(const scalar_operator &other, operator_sum &&self); - template - friend operator_sum operator-(const scalar_operator &other, const operator_sum &self); - template - friend operator_sum operator-(const scalar_operator &other, operator_sum &&self); + template + friend operator_sum operator*(std::complex other, + const operator_sum &self); + template + friend operator_sum operator*(std::complex other, + operator_sum &&self); + template + friend operator_sum operator+(std::complex other, + const operator_sum &self); + template + friend operator_sum operator+(std::complex other, + operator_sum &&self); + template + friend operator_sum operator-(std::complex other, + const operator_sum &self); + template + friend operator_sum operator-(std::complex other, + operator_sum &&self); + template + friend operator_sum operator*(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator*(const scalar_operator &other, + operator_sum &&self); + template + friend operator_sum operator+(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator+(const scalar_operator &other, + operator_sum &&self); + template + friend operator_sum operator-(const scalar_operator &other, + const operator_sum &self); + template + friend operator_sum operator-(const scalar_operator &other, + operator_sum &&self); // common operators diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h index 9be42f03e8..d6207e08bc 100644 --- a/runtime/cudaq/schedule.h +++ b/runtime/cudaq/schedule.h @@ -73,7 +73,7 @@ class Schedule { Schedule(const std::vector &steps, const std::vector ¶meters = {}, std::function( - const std::unordered_map> &)> + const std::unordered_map> &)> value_function = {}); }; } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/CuDensityMatState.cpp b/runtime/nvqir/cudensitymat/CuDensityMatState.cpp index 6b926fd586..f4bc653348 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatState.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatState.cpp @@ -505,9 +505,8 @@ cudensitymatHandle_t cudaq::CuDensityMatState::get_handle() const { return cudmHandle; } -void CuDensityMatState::initialize_cudm( - cudensitymatHandle_t handleToSet, - const std::vector &dims) { +void CuDensityMatState::initialize_cudm(cudensitymatHandle_t handleToSet, + const std::vector &dims) { cudmHandle = handleToSet; hilbertSpaceDims = dims; size_t expectedDensityMatrixSize = diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 0be2e7b0d1..6cd7f09c7a 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -9,6 +9,8 @@ #include "cudm_helpers.h" #include "common/Logger.h" #include "cudm_error_handling.h" +#include + using namespace cudaq; namespace cudaq { @@ -16,6 +18,7 @@ cudm_helper::cudm_helper(cudensitymatHandle_t handle) : handle(handle) {} cudm_helper::~cudm_helper() { cudaDeviceSynchronize(); + for (auto term : m_operatorTerms) { cudensitymatDestroyOperatorTerm(term); } @@ -34,16 +37,18 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { "scalar_operator does not have a valid generator function."); } - auto callback = [](double time, int32_t num_params, const double params[], - cudaDataType_t data_type, - void *scalar_storage) -> int32_t { + auto *stored_scalar_op = new scalar_operator(scalar_op); + + using WrapperFuncType = + int32_t (*)(cudensitymatScalarCallback_t, double, int32_t, const double[], + cudaDataType_t, void *); + + auto wrapper = [](cudensitymatScalarCallback_t callback, double time, + int32_t num_params, const double params[], + cudaDataType_t data_type, void *scalar_storage) -> int32_t { try { - std::cout << "Time = " << time << "\n"; - std::cout << "Params: "; - for (int i = 0; i < num_params; ++i) - std::cout << params[i] << " "; - std::cout << "\n"; - std::cout << "scalar_storage = " << scalar_storage << "\n"; + scalar_operator *stored_op = + reinterpret_cast(callback); std::unordered_map> param_map; // FIXME: Figure how to populate the param map @@ -52,12 +57,10 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { param_map[std::to_string(i)] = params[i]; } param_map["time"] = time; - // TODO: figure out how to capture the scalar_op. - // Note: cudensitymat expects a pure C function pointer, no state.... - // std::complex result = scalar_op.evaluate(param_map); - // std::cout << "Result = " << result << "\n"; + + std::complex result = stored_op->evaluate(param_map); auto *tdCoef = static_cast *>(scalar_storage); - *tdCoef = 2.0; + *tdCoef = result; return CUDENSITYMAT_STATUS_SUCCESS; } catch (const std::exception &e) { std::cerr << "Error in scalar callback: " << e.what() << std::endl; @@ -66,9 +69,10 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { }; cudensitymatWrappedScalarCallback_t wrappedCallback; - wrappedCallback.callback = callback; - // Note: the wrapper must be nullptr - wrappedCallback.wrapper = nullptr; + wrappedCallback.callback = + reinterpret_cast(stored_scalar_op); + wrappedCallback.wrapper = + reinterpret_cast(static_cast(wrapper)); return wrappedCallback; } diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index 897470e6f8..30f77b14a2 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -6,12 +6,12 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatState.h" #include "common/EigenDense.h" #include "test_mocks.h" #include #include #include -#include "CuDensityMatState.h" #include #include #include @@ -47,7 +47,8 @@ TEST_F(CuDensityExpectationTest, checkCompute) { for (std::size_t stateIdx = 0; stateIdx < dims[0]; ++stateIdx) { std::vector> initialState(dims[0], 0.0); initialState[stateIdx] = 1.0; - auto inputState = std::make_unique(handle_, initialState, dims); + auto inputState = + std::make_unique(handle_, initialState, dims); expectation.prepare(inputState->get_impl()); const auto expVal = expectation.compute(inputState->get_impl(), 0.0); EXPECT_NEAR(expVal.real(), 1.0 * stateIdx, 1e-12); @@ -75,7 +76,8 @@ TEST_F(CuDensityExpectationTest, checkCompositeSystem) { std::vector> initialState( initial_state_vec.data(), initial_state_vec.data() + initial_state_vec.size()); - auto inputState = std::make_unique(handle_, initialState, dims); + auto inputState = + std::make_unique(handle_, initialState, dims); expectation.prepare(inputState->get_impl()); const auto expVal = expectation.compute(inputState->get_impl(), 0.0); std::cout << "Result: " << expVal << "\n"; diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index f4eea77005..ea384e33dd 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -6,11 +6,11 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatState.h" #include "test_mocks.h" #include #include #include -#include "CuDensityMatState.h" #include // Initialize operator_sum @@ -32,7 +32,8 @@ class CuDensityMatHelpersTestFixture : public ::testing::Test { std::vector mode_extents = {2}; std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - state = std::make_unique(handle, rawData, mode_extents); + state = std::make_unique(handle, rawData, + mode_extents); } void TearDown() override { HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); } diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index f77e894cf8..7e554864cd 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -6,10 +6,10 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatState.h" #include #include #include -#include "CuDensityMatState.h" #include #include #include @@ -119,8 +119,9 @@ TEST_F(CuDensityMatStateTest, ConversionForSingleQubitSystem) { TEST_F(CuDensityMatStateTest, InvalidHilbertSpaceDims) { // 3x3 space is not supported by the provided rawData size hilbertSpaceDims = {3, 3}; - EXPECT_THROW(CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims), - std::invalid_argument); + EXPECT_THROW( + CuDensityMatState state(handle, stateVectorData, hilbertSpaceDims), + std::invalid_argument); } TEST_F(CuDensityMatStateTest, ValidDensityMatrixState) { diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index b341b61676..898365dd5e 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -84,11 +84,11 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepNoLiouvillian) { // Compute step with mismatched dimensions TEST_F(CuDensityMatTimeStepperTest, ComputeStepMistmatchedDimensions) { - EXPECT_THROW(std::unique_ptr mismatchedState = - std::make_unique(handle_, - mock_initial_state_data(), - std::vector{3, 3}), - std::invalid_argument); + EXPECT_THROW( + std::unique_ptr mismatchedState = + std::make_unique( + handle_, mock_initial_state_data(), std::vector{3, 3}), + std::invalid_argument); } // Compute step with zero step size @@ -150,9 +150,6 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { auto time_stepper = std::make_unique(handle_, cudm_lindblad_op); auto output_state = time_stepper->compute(input_state, 0.0, 1.0, {}); - std::cout << "Printing output_state ..." << std::endl; - output_state.dump(std::cout); - std::vector> output_state_vec(100); output_state.to_host(output_state_vec.data(), output_state_vec.size()); @@ -179,7 +176,7 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { castSimState->initialize_cudm(handle_, dims); auto function = [](const std::unordered_map> ¶meters) { - auto entry = parameters.find("alpha"); + auto entry = parameters.find("time"); if (entry == parameters.end()) throw std::runtime_error("Cannot find time value"); return entry->second; @@ -187,10 +184,12 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { auto op = cudaq::scalar_operator(function) * cudaq::matrix_operator::create(0); auto cudmOp = - helper_->convert_to_cudensitymat_operator( - {}, op, dims); // Initialize the time stepper + helper_->convert_to_cudensitymat_operator({}, op, + dims); + // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); - auto outputState = time_stepper->compute(inputState, 1.0, 1.0, {{"alpha", 2.0}}); + auto outputState = + time_stepper->compute(inputState, 1.0, 1.0, {{"time", 2.0}}); outputState.dump(std::cout); std::vector> outputStateVec(4); outputState.to_host(outputStateVec.data(), outputStateVec.size()); diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index bd36537a89..cf6eb10065 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -32,8 +32,8 @@ class RungeKuttaIntegratorTest : public ::testing::Test { liouvillian_ = mock_liouvillian(handle_); // Create initial state - state_ = std::make_unique(handle_, mock_initial_state_data(), - mock_hilbert_space_dims()); + state_ = std::make_unique( + handle_, mock_initial_state_data(), mock_hilbert_space_dims()); ASSERT_NE(state_, nullptr); ASSERT_TRUE(state_->is_initialized()); @@ -149,10 +149,11 @@ TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { cudm_helper helper(handle_); const std::vector> initialStateVec = {{1.0, 0.0}, - {0.0, 0.0}}; + {0.0, 0.0}}; const std::vector dims = {2}; auto spin_op_x = cudaq::spin_operator::x(0); - cudaq::product_operator ham1 = 2.0 * M_PI * 0.1 * spin_op_x; + cudaq::product_operator ham1 = + 2.0 * M_PI * 0.1 * spin_op_x; cudaq::operator_sum ham(ham1); SystemDynamics system; system.hamiltonian = &ham; From aa8d3f06f10923d4549d59be93b32d207f95376c Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 18 Feb 2025 09:25:23 -0800 Subject: [PATCH 286/311] Removing mutex Signed-off-by: Sachin Pisal --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 6cd7f09c7a..1782dfbc64 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -9,7 +9,6 @@ #include "cudm_helpers.h" #include "common/Logger.h" #include "cudm_error_handling.h" -#include using namespace cudaq; From 272f436f86fa8fe3efea5ee13fa66636c86c4766 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Wed, 19 Feb 2025 00:47:14 +0000 Subject: [PATCH 287/311] Passing param names to scalar callback Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 68 ++++++++++--------- runtime/nvqir/cudensitymat/cudm_helpers.h | 6 +- unittests/dynamics/test_cudm_helpers.cpp | 16 ----- unittests/dynamics/test_cudm_time_stepper.cpp | 31 +++++---- 4 files changed, 58 insertions(+), 63 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 1782dfbc64..6a16af116c 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -9,6 +9,7 @@ #include "cudm_helpers.h" #include "common/Logger.h" #include "cudm_error_handling.h" +#include using namespace cudaq; @@ -29,15 +30,25 @@ cudm_helper::~cudm_helper() { } } +struct ScalarCallBackContext { + scalar_operator scalarOp; + std::vector paramNames; + ScalarCallBackContext(const scalar_operator &scalar_op, + const std::vector ¶mNames) + : scalarOp(scalar_op), paramNames(paramNames){}; +}; + cudensitymatWrappedScalarCallback_t -cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { +cudm_helper::_wrap_callback(const scalar_operator &scalar_op, + const std::vector ¶mNames) { if (scalar_op.is_constant()) { throw std::runtime_error( "scalar_operator does not have a valid generator function."); } - auto *stored_scalar_op = new scalar_operator(scalar_op); - + // FIXME: leak + auto *stored_callback_context = + new ScalarCallBackContext(scalar_op, paramNames); using WrapperFuncType = int32_t (*)(cudensitymatScalarCallback_t, double, int32_t, const double[], cudaDataType_t, void *); @@ -46,18 +57,27 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { int32_t num_params, const double params[], cudaDataType_t data_type, void *scalar_storage) -> int32_t { try { - scalar_operator *stored_op = - reinterpret_cast(callback); + ScalarCallBackContext *context = + reinterpret_cast(callback); + scalar_operator &stored_op = context->scalarOp; + if (num_params != 2 * context->paramNames.size()) + throw std::runtime_error( + fmt::format("[Internal Error] Invalid number of callback " + "parameters encountered. Expected {} double params " + "representing {} complex values but received {}.", + 2 * context->paramNames.size(), + context->paramNames.size(), num_params)); std::unordered_map> param_map; - // FIXME: Figure how to populate the param map - // This param map was sorted in cudmStepper::compute - for (size_t i = 0; i < num_params; i++) { - param_map[std::to_string(i)] = params[i]; + for (size_t i = 0; i < context->paramNames.size(); ++i) { + param_map[context->paramNames[i]] = + std::complex(params[2 * i], params[2 * i + 1]); + cudaq::debug("Callback param name {}, value {}", context->paramNames[i], + param_map[context->paramNames[i]]); } - param_map["time"] = time; - std::complex result = stored_op->evaluate(param_map); + std::complex result = stored_op.evaluate(param_map); + cudaq::debug("Scalar callback evaluated result = {}", result); auto *tdCoef = static_cast *>(scalar_storage); *tdCoef = result; return CUDENSITYMAT_STATUS_SUCCESS; @@ -69,7 +89,7 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op) { cudensitymatWrappedScalarCallback_t wrappedCallback; wrappedCallback.callback = - reinterpret_cast(stored_scalar_op); + reinterpret_cast(stored_callback_context); wrappedCallback.wrapper = reinterpret_cast(static_cast(wrapper)); return wrappedCallback; @@ -290,24 +310,6 @@ void cudm_helper::append_elementary_operator_to_term( make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); } -// Function to create and append a scalar to a term -void cudm_helper::append_scalar_to_term(cudensitymatOperatorTerm_t term, - const scalar_operator &scalar_op) { - cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - - if (scalar_op.is_constant()) { - std::complex coeff = scalar_op.evaluate({}); - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 0, nullptr, nullptr, nullptr, - {make_cuDoubleComplex(coeff.real(), coeff.imag())}, wrapped_callback)); - } else { - wrapped_callback = _wrap_callback(scalar_op); - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, 0, nullptr, nullptr, nullptr, - {make_cuDoubleComplex(1.0, 0.0)}, wrapped_callback)); - } -} - void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, cudaStream_t stream) { if (!state) { @@ -603,6 +605,10 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( handle, static_cast(mode_extents.size()), mode_extents.data(), &operator_handle)); + const std::map> sortedParameters( + parameters.begin(), parameters.end()); + auto ks = std::views::keys(sortedParameters); + const std::vector keys{ks.begin(), ks.end()}; for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; @@ -613,7 +619,7 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), wrapped_callback)); } else { - wrapped_callback = _wrap_callback(coeff); + wrapped_callback = _wrap_callback(coeff, keys); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), wrapped_callback)); diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index 9342989d1e..bb4dffff4a 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -77,13 +77,11 @@ class cudm_helper { // Callback Wrappers static cudensitymatWrappedScalarCallback_t - _wrap_callback(const scalar_operator &scalar_op); + _wrap_callback(const scalar_operator &scalar_op, + const std::vector ¶mNames); static cudensitymatWrappedTensorCallback_t _wrap_tensor_callback(const matrix_operator &op); - // Elementary Operator Functions - void append_scalar_to_term(cudensitymatOperatorTerm_t term, - const scalar_operator &scalar_op); cudensitymatElementaryOperator_t create_elementary_operator( const cudaq::matrix_operator &elem_op, const std::unordered_map> ¶meters, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index ea384e33dd..6dc4e92106 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -179,19 +179,3 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { // cudensitymatDestroyOperator(result); // }); } - -// Test for appending a scalar to a term -TEST_F(CuDensityMatHelpersTestFixture, AppendScalarToTerm) { - cudensitymatOperatorTerm_t term; - std::vector mode_extents = {2, 2}; - - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &term)); - - cudaq::scalar_operator scalar_op(2.0); - - EXPECT_NO_THROW(helper->append_scalar_to_term(term, scalar_op)); - - cudensitymatDestroyOperatorTerm(term); -} diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 898365dd5e..0dcf0a56a4 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -174,28 +174,35 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { auto *castSimState = dynamic_cast(simState); EXPECT_TRUE(castSimState != nullptr); castSimState->initialize_cudm(handle_, dims); - auto function = [](const std::unordered_map> - ¶meters) { - auto entry = parameters.find("time"); - if (entry == parameters.end()) - throw std::runtime_error("Cannot find time value"); - return entry->second; - }; + const std::string paramName = "alpha"; + const std::complex paramValue{2.0, 3.0}; + std::unordered_map> params{ + {paramName, paramValue}}; + + auto function = + [paramName](const std::unordered_map> + ¶meters) { + auto entry = parameters.find(paramName); + if (entry == parameters.end()) + throw std::runtime_error( + "Cannot find value of expected parameter named " + paramName); + return entry->second; + }; + auto op = cudaq::scalar_operator(function) * cudaq::matrix_operator::create(0); auto cudmOp = - helper_->convert_to_cudensitymat_operator({}, op, - dims); + helper_->convert_to_cudensitymat_operator( + params, op, dims); // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); - auto outputState = - time_stepper->compute(inputState, 1.0, 1.0, {{"time", 2.0}}); + auto outputState = time_stepper->compute(inputState, 1.0, 1.0, params); outputState.dump(std::cout); std::vector> outputStateVec(4); outputState.to_host(outputStateVec.data(), outputStateVec.size()); // Create operator move the state up 1 step. const std::vector> expectedOutputState = { - {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + {0.0, 0.0}, paramValue, {0.0, 0.0}, {0.0, 0.0}}; for (std::size_t i = 0; i < expectedOutputState.size(); ++i) { EXPECT_TRUE(std::abs(expectedOutputState[i] - outputStateVec[i]) < 1e-12); From 052118370b36870a1cf8915d90b8521e53c32247 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Wed, 19 Feb 2025 02:21:10 +0000 Subject: [PATCH 288/311] integrator + schedule in the loop to resolve parameter values Signed-off-by: Thien Nguyen --- runtime/cudaq/dynamics/schedule.cpp | 17 ++++-- runtime/cudaq/dynamics_integrators.h | 3 +- runtime/cudaq/schedule.h | 10 ++-- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 2 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 15 +++++- .../cudensitymat/runge_kutta_integrator.cpp | 46 ++++++++++++---- unittests/dynamics/test_evolve_single.cpp | 53 +++++++++++++++++++ .../dynamics/test_runge_kutta_integrator.cpp | 4 +- 8 files changed, 127 insertions(+), 23 deletions(-) diff --git a/runtime/cudaq/dynamics/schedule.cpp b/runtime/cudaq/dynamics/schedule.cpp index 1deb469748..a5d2dbc4d9 100644 --- a/runtime/cudaq/dynamics/schedule.cpp +++ b/runtime/cudaq/dynamics/schedule.cpp @@ -16,8 +16,19 @@ namespace cudaq { Schedule::Schedule( const std::vector &steps, const std::vector ¶meters, - std::function( - const std::unordered_map> &)> + std::function(const std::string &, double)> value_function) - : _steps(steps), _parameters(parameters), _value_function(value_function) {} + : _steps(steps), _parameters(parameters), _value_function(value_function) { + if (!_value_function) { + printf("HEY\n"); + _value_function = [&](const std::string ¶mName, + double value) -> std::complex { + if (std::find(_parameters.begin(), _parameters.end(), paramName) == + _parameters.end()) + throw std::runtime_error("Unknown parameter named " + paramName); + + return value; + }; + } +} } // namespace cudaq diff --git a/runtime/cudaq/dynamics_integrators.h b/runtime/cudaq/dynamics_integrators.h index fafc485cbc..07e4164e8e 100644 --- a/runtime/cudaq/dynamics_integrators.h +++ b/runtime/cudaq/dynamics_integrators.h @@ -32,12 +32,13 @@ class runge_kutta : public BaseIntegrator { void integrate(double target_time) override; void set_state(cudaq::state initial_state, double t0) override; std::pair get_state() override; - void set_system(const SystemDynamics &system); + void set_system(const SystemDynamics &system, const cudaq::Schedule& schedule); private: double m_t; std::shared_ptr m_state; SystemDynamics m_system; std::unique_ptr m_stepper; + cudaq::Schedule m_schedule; }; } // namespace cudaq diff --git a/runtime/cudaq/schedule.h b/runtime/cudaq/schedule.h index d6207e08bc..5b47ec33f9 100644 --- a/runtime/cudaq/schedule.h +++ b/runtime/cudaq/schedule.h @@ -23,8 +23,7 @@ class Schedule { private: std::vector _steps; std::vector _parameters; - std::function( - const std::unordered_map> &)> + std::function(const std::string &, double)> _value_function; public: @@ -54,8 +53,7 @@ class Schedule { const std::vector ¶meters() const { return _parameters; } - std::function( - const std::unordered_map> &)> + std::function(const std::string &, double)> value_function() const { return _value_function; } @@ -72,8 +70,8 @@ class Schedule { /// _current_idx will be used to track the position in the sequence of steps. Schedule(const std::vector &steps, const std::vector ¶meters = {}, - std::function( - const std::unordered_map> &)> + std::function(const std::string &, double)> value_function = {}); + Schedule() = default; }; } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 5989aacec4..46eaf7e834 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -47,7 +47,7 @@ evolve_result evolve_single( const_cast *>(&hamiltonian); system.collapseOps = collapse_operators; system.modeExtents = dims; - integrator.set_system(system); + integrator.set_system(system, schedule); integrator.set_state(initial_state, 0.0); diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 6a16af116c..f7f6bda561 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -649,6 +649,10 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( &liouvillian)); // Append an operator term to the operator (super-operator) // Handle the Hamiltonian + const std::map> sortedParameters( + parameters.begin(), parameters.end()); + auto ks = std::views::keys(sortedParameters); + const std::vector keys{ks.begin(), ks.end()}; for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (coeff.is_constant()) { @@ -667,7 +671,16 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( make_cuDoubleComplex(rightCoeff.real(), rightCoeff.imag()), wrapped_callback)); } else { - throw std::runtime_error("TODO: implement callback"); + wrapped_callback = _wrap_callback(coeff, keys); + // -i constant (left multiplication) + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, term, 0, make_cuDoubleComplex(0.0, -1.0), + wrapped_callback)); + + // +i constant (right multiplication, i.e., dual) + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, term, 1, make_cuDoubleComplex(0.0, 1.0), + wrapped_callback)); } } diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index 4568d0c3b2..e4823e23af 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -13,8 +13,10 @@ #include "cudm_time_stepper.h" namespace cudaq { -void runge_kutta::set_system(const SystemDynamics &system) { +void runge_kutta::set_system(const SystemDynamics &system, + const cudaq::Schedule &schedule) { m_system = system; + m_schedule = schedule; } void runge_kutta::set_state(cudaq::state initial_state, double t0) { @@ -44,14 +46,19 @@ void runge_kutta::integrate(double target_time) { return castSimState; }; auto &castSimState = *asCudmState(*m_state); + std::unordered_map> params; if (!m_stepper) { static std::unordered_map> helpers; if (helpers.find(castSimState.get_handle()) == helpers.end()) helpers[castSimState.get_handle()] = std::make_unique(castSimState.get_handle()); auto &helper = *(helpers.find(castSimState.get_handle())->second); + for (const auto ¶m : m_schedule.parameters()) { + params[param] = m_schedule.value_function()(param, 0.0); + } + auto liouvillian = helper.construct_liouvillian( - *m_system.hamiltonian, m_system.collapseOps, m_system.modeExtents, {}, + *m_system.hamiltonian, m_system.collapseOps, m_system.modeExtents, params, castSimState.is_density_matrix()); m_stepper = std::make_unique(castSimState.get_handle(), liouvillian); @@ -66,49 +73,68 @@ void runge_kutta::integrate(double target_time) { if (substeps == 1) { // Euler method (1st order) - auto k1State = m_stepper->compute(*m_state, m_t, step_size, {}); + for (const auto ¶m : m_schedule.parameters()) { + params[param] = m_schedule.value_function()(param, m_t); + } + auto k1State = m_stepper->compute(*m_state, m_t, step_size, params); auto &k1 = *asCudmState(k1State); // k1.dump(std::cout); k1 *= step_size; castSimState += k1; } else if (substeps == 2) { // Midpoint method (2nd order) - auto k1State = m_stepper->compute(*m_state, m_t, step_size, {}); + for (const auto ¶m : m_schedule.parameters()) { + params[param] = m_schedule.value_function()(param, m_t); + } + auto k1State = m_stepper->compute(*m_state, m_t, step_size, params); auto &k1 = *asCudmState(k1State); k1 *= (step_size / 2.0); castSimState += k1; - + for (const auto ¶m : m_schedule.parameters()) { + params[param] = + m_schedule.value_function()(param, m_t + step_size / 2.0); + } auto k2State = - m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size, {}); + m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size, params); auto &k2 = *asCudmState(k2State); k2 *= (step_size / 2.0); castSimState += k2; } else if (substeps == 4) { // Runge-Kutta method (4th order) - auto k1State = m_stepper->compute(*m_state, m_t, step_size, {}); + for (const auto ¶m : m_schedule.parameters()) { + params[param] = m_schedule.value_function()(param, m_t); + } + auto k1State = m_stepper->compute(*m_state, m_t, step_size, params); auto &k1 = *asCudmState(k1State); CuDensityMatState rho_temp = CuDensityMatState::clone(castSimState); rho_temp += (k1 * (step_size / 2)); + for (const auto ¶m : m_schedule.parameters()) { + params[param] = + m_schedule.value_function()(param, m_t + step_size / 2.0); + } auto k2State = m_stepper->compute( cudaq::state(new CuDensityMatState(std::move(rho_temp))), - m_t + step_size / 2.0, step_size, {}); + m_t + step_size / 2.0, step_size, params); auto &k2 = *asCudmState(k2State); CuDensityMatState rho_temp_2 = CuDensityMatState::clone(castSimState); rho_temp_2 += (k2 * (step_size / 2)); auto k3State = m_stepper->compute( cudaq::state(new CuDensityMatState(std::move(rho_temp_2))), - m_t + step_size / 2.0, step_size, {}); + m_t + step_size / 2.0, step_size, params); auto &k3 = *asCudmState(k3State); CuDensityMatState rho_temp_3 = CuDensityMatState::clone(castSimState); rho_temp_3 += (k3 * step_size); + for (const auto ¶m : m_schedule.parameters()) { + params[param] = m_schedule.value_function()(param, m_t + step_size); + } auto k4State = m_stepper->compute( cudaq::state(new CuDensityMatState(std::move(rho_temp_3))), - m_t + step_size, step_size, {}); + m_t + step_size, step_size, params); auto &k4 = *asCudmState(k4State); castSimState += (k1 + k2 * 2.0 + k3 * 2.0 + k4) * (step_size / 6.0); } else { diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index fae5ec48f1..0fde3e9a85 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -230,4 +230,57 @@ TEST(EvolveTester, checkCompositeSystemWithCollapse) { << " vs " << expectedResult << "\n"; EXPECT_NEAR(totalParticleCount, expectedResult, 0.1); } +} + +TEST(EvolveTester, checkScalarTd) { + const std::map dims = {{0, 10}}; + + constexpr int numSteps = 101; + const auto steps = cudaq::linspace(0.0, 10.0, numSteps); + cudaq::Schedule schedule(steps, {"t"}); + + auto function = [](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("t"); + if (entry == parameters.end()) + throw std::runtime_error("Cannot find value of expected parameter"); + return 1.0; + }; + cudaq::product_operator ham1 = + cudaq::scalar_operator(function) * cudaq::boson_operator::number(0); + cudaq::operator_sum ham(ham1); + cudaq::product_operator obs1 = + cudaq::boson_operator::number(0); + cudaq::operator_sum obs(obs1); + const double decayRate = 0.1; + cudaq::product_operator collapseOp1 = + std::sqrt(decayRate) * cudaq::boson_operator::annihilate(0); + cudaq::operator_sum collapseOp(collapseOp1); + Eigen::VectorXcd initial_state_vec = Eigen::VectorXcd::Zero(10); + initial_state_vec[9] = 1.0; + Eigen::MatrixXcd rho0 = initial_state_vec * initial_state_vec.transpose(); + auto initialState = + cudaq::state::from_data(std::make_pair(rho0.data(), rho0.size())); + cudaq::runge_kutta integrator; + integrator.dt = 0.001; + integrator.order = 4; + auto result = cudaq::evolve_single(ham, dims, schedule, initialState, + integrator, {&collapseOp}, {&obs}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + int idx = 0; + for (const auto &t : schedule) { + const double expected = 9.0 * std::exp(-decayRate * steps[idx++]); + theoryResults.emplace_back(expected); + } + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + std::cout << "Result = " << (double)expVals[0] + << "; expected = " << theoryResults[count] << "\n"; + EXPECT_NEAR((double)expVals[0], theoryResults[count], 1e-3); + count++; + } } \ No newline at end of file diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index cf6eb10065..c7c7a2e307 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -173,7 +173,9 @@ TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { EXPECT_TRUE(castSimState != nullptr); castSimState->initialize_cudm(handle_, dims); integrator.set_state(initialState, 0.0); - integrator.set_system(system); + cudaq::Schedule schedule( + cudaq::linspace(0, 1.0 * numDataPoints, numDataPoints)); + integrator.set_system(system, schedule); std::vector> outputStateVec(2); for (std::size_t i = 1; i < numDataPoints; ++i) { integrator.integrate(i); From 1a652bfcc6058b53115eb96d9580838ebfd3efd2 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 18 Feb 2025 18:58:36 -0800 Subject: [PATCH 289/311] Adding tensor callback with the unittest Signed-off-by: Sachin Pisal --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 78 ++++++++++++++----- runtime/nvqir/cudensitymat/cudm_helpers.h | 9 ++- unittests/dynamics/test_cudm_helpers.cpp | 2 +- unittests/dynamics/test_cudm_time_stepper.cpp | 54 +++++++++++++ 4 files changed, 120 insertions(+), 23 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index f7f6bda561..d98c34ccc8 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -38,6 +38,15 @@ struct ScalarCallBackContext { : scalarOp(scalar_op), paramNames(paramNames){}; }; +struct TensorCallBackContext { + matrix_operator tensorOp; + std::vector paramNames; + + TensorCallBackContext(const matrix_operator &tensor_op, + const std::vector ¶m_names) + : tensorOp(tensor_op), paramNames(param_names){}; +}; + cudensitymatWrappedScalarCallback_t cudm_helper::_wrap_callback(const scalar_operator &scalar_op, const std::vector ¶mNames) { @@ -96,22 +105,45 @@ cudm_helper::_wrap_callback(const scalar_operator &scalar_op, } cudensitymatWrappedTensorCallback_t -cudm_helper::_wrap_tensor_callback(const matrix_operator &op) { - auto callback = - [](cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, - const int64_t mode_extents[], const int32_t diagonal_offsets[], - double time, int32_t num_params, const double params[], - cudaDataType_t data_type, void *tensor_storage) -> int32_t { +cudm_helper::_wrap_tensor_callback(const matrix_operator &op, + const std::vector ¶mNames) { + auto *stored_callback_context = new TensorCallBackContext(op, paramNames); + + using WrapperFuncType = int32_t (*)( + cudensitymatTensorCallback_t, cudensitymatElementaryOperatorSparsity_t, + int32_t, const int64_t[], const int32_t[], double, int64_t, int32_t, + const double[], cudaDataType_t, void *, cudaStream_t); + + auto wrapper = [](cudensitymatTensorCallback_t callback, + cudensitymatElementaryOperatorSparsity_t sparsity, + int32_t num_modes, const int64_t mode_extents[], + const int32_t diagonal_offsets[], double time, + int64_t batch_size, int32_t num_params, + const double params[], cudaDataType_t data_type, + void *tensor_storage, cudaStream_t stream) -> int32_t { try { - matrix_operator *mat_op = static_cast(tensor_storage); + auto *context = reinterpret_cast(callback); + matrix_operator &stored_op = context->tensorOp; + + // if (num_params != 2 * context->paramNames.size()) + // throw std::runtime_error( + // fmt::format("[Internal Error] Invalid number of tensor callback " + // "parameters. Expected {} double values " + // "representing {} complex parameters but received + // {}.", std::to_string(2 * context->paramNames.size()), + // std::to_string(context->paramNames.size()), + // std::to_string(num_params))); std::unordered_map> param_map; - for (size_t i = 0; i < num_params; i++) { - param_map[std::to_string(i)] = params[i]; + for (size_t i = 0; i < context->paramNames.size(); ++i) { + param_map[context->paramNames[i]] = + std::complex(params[2 * i], params[2 * i + 1]); + cudaq::debug("Tensor callback param name {}, value {}", + context->paramNames[i], param_map[context->paramNames[i]]); } std::unordered_map dimensions = {}; - matrix_2 matrix_data = mat_op->to_matrix(dimensions, param_map); + matrix_2 matrix_data = stored_op.to_matrix(dimensions, param_map); std::size_t rows = matrix_data.get_rows(); std::size_t cols = matrix_data.get_columns(); @@ -121,8 +153,7 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op) { } if (data_type == CUDA_C_64F) { - cuDoubleComplex *storage = - static_cast(tensor_storage); + auto *storage = static_cast(tensor_storage); for (size_t i = 0; i < rows; i++) { for (size_t j = 0; j < cols; j++) { storage[i * cols + j] = make_cuDoubleComplex( @@ -130,7 +161,7 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op) { } } } else if (data_type == CUDA_C_32F) { - cuFloatComplex *storage = static_cast(tensor_storage); + auto *storage = static_cast(tensor_storage); for (size_t i = 0; i < rows; i++) { for (size_t j = 0; j < cols; j++) { storage[i * cols + j] = make_cuFloatComplex( @@ -150,8 +181,10 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op) { }; cudensitymatWrappedTensorCallback_t wrapped_callback; - wrapped_callback.callback = callback; - wrapped_callback.wrapper = new matrix_operator(op); + wrapped_callback.callback = + reinterpret_cast(stored_callback_context); + wrapped_callback.wrapper = + reinterpret_cast(static_cast(wrapper)); return wrapped_callback; } @@ -224,7 +257,12 @@ cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( nullptr}; if (!parameters.empty()) { - wrapped_tensor_callback = _wrap_tensor_callback(elem_op); + const std::map> sortedParameters( + parameters.begin(), parameters.end()); + auto ks = std::views::keys(sortedParameters); + const std::vector keys{ks.begin(), ks.end()}; + + wrapped_tensor_callback = _wrap_tensor_callback(elem_op, keys); } auto *elementaryMat_d = create_array_gpu(flat_matrix); @@ -555,6 +593,7 @@ cudm_helper::convert_dimensions(const std::vector &mode_extents) { std::vector> cudm_helper::convert_to_cudensitymat( const operator_sum &op, + const std::unordered_map> ¶meters, const std::vector &mode_extents) { if (op.get_terms().empty()) { throw std::invalid_argument("Operator sum cannot be empty."); @@ -577,7 +616,7 @@ cudm_helper::convert_to_cudensitymat( if (const auto *elem_op = dynamic_cast(&component)) { auto cudm_elem_op = - create_elementary_operator(*elem_op, {}, mode_extents); + create_elementary_operator(*elem_op, parameters, mode_extents); elem_ops.emplace_back(cudm_elem_op); all_degrees.emplace_back(elem_op->degrees()); } else { @@ -609,7 +648,8 @@ cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( parameters.begin(), parameters.end()); auto ks = std::views::keys(sortedParameters); const std::vector keys{ks.begin(), ks.end()}; - for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { + for (auto &[coeff, term] : + convert_to_cudensitymat(op, parameters, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (coeff.is_constant()) { @@ -653,7 +693,7 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( parameters.begin(), parameters.end()); auto ks = std::views::keys(sortedParameters); const std::vector keys{ks.begin(), ks.end()}; - for (auto &[coeff, term] : convert_to_cudensitymat(op, mode_extents)) { + for (auto &[coeff, term] : convert_to_cudensitymat(op, parameters, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (coeff.is_constant()) { const auto coeffVal = coeff.evaluate(); diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index bb4dffff4a..4476b9a4bc 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -46,8 +46,10 @@ class cudm_helper { const std::vector &mode_extents); std::vector> - convert_to_cudensitymat(const operator_sum &op, - const std::vector &mode_extents); + convert_to_cudensitymat( + const operator_sum &op, + const std::unordered_map> ¶meters, + const std::vector &mode_extents); // Construct Liouvillian cudensitymatOperator_t construct_liouvillian( const operator_sum &op, @@ -80,7 +82,8 @@ class cudm_helper { _wrap_callback(const scalar_operator &scalar_op, const std::vector ¶mNames); static cudensitymatWrappedTensorCallback_t - _wrap_tensor_callback(const matrix_operator &op); + _wrap_tensor_callback(const matrix_operator &op, + const std::vector ¶mNames); cudensitymatElementaryOperator_t create_elementary_operator( const cudaq::matrix_operator &elem_op, diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index 6dc4e92106..b097d51cb1 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -165,7 +165,7 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { cudaq::matrix_operator::instantiate(op_id, {0}).get_terms()[0]; auto wrapped_tensor_callback = - cudaq::cudm_helper::_wrap_tensor_callback(matrix_op); + cudaq::cudm_helper::_wrap_tensor_callback(matrix_op, {}); ASSERT_NE(wrapped_tensor_callback.callback, nullptr); diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 0dcf0a56a4..285bb7bcdd 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -209,3 +209,57 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { } HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); } + +TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { + const std::vector> initialState = { + {1.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; + const std::vector dims = {4}; + auto inputState = cudaq::state::from_data(initialState); + auto *simState = cudaq::state_helper::getSimulationState(&inputState); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); + + const std::string paramName = "beta"; + const std::complex paramValue{2.0, 3.0}; + std::unordered_map> params{ + {paramName, paramValue}}; + + auto tensorFunction = + [paramName](const std::vector &dimensions, + const std::unordered_map> + ¶meters) -> matrix_2 { + auto entry = parameters.find(paramName); + if (entry == parameters.end()) + throw std::runtime_error( + "Cannot find value of expected parameter named " + paramName); + + std::complex value = entry->second; + matrix_2 mat(2, 2); + mat[{0, 0}] = value; + mat[{1, 1}] = std::conj(value); + mat[{0, 1}] = {0.0, 0.0}; + mat[{1, 0}] = {0.0, 0.0}; + return mat; + }; + + matrix_operator::define("CustomTensorOp", {-1}, tensorFunction); + auto op = cudaq::matrix_operator::instantiate("CustomTensorOp", {0}); + auto cudmOp = + helper_->convert_to_cudensitymat_operator( + params, op, dims); + // Initialize the time stepper + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(inputState, 1.0, 1.0, params); + outputState.dump(std::cout); + std::vector> outputStateVec(4); + outputState.to_host(outputStateVec.data(), outputStateVec.size()); + // Create operator move the state up 1 step. + const std::vector> expectedOutputState = { + paramValue, {0.0, 0.0}, {0.0, 0.0}, std::conj(paramValue)}; + + for (std::size_t i = 0; i < expectedOutputState.size(); ++i) { + EXPECT_TRUE(std::abs(expectedOutputState[i] - outputStateVec[i]) < 1e-12); + } + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); +} From 0784b456a339ca5e305892d7d56c68c759053084 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Wed, 19 Feb 2025 03:54:11 +0000 Subject: [PATCH 290/311] Support state vec -> density matrix conversion if collapse ops are present Signed-off-by: Thien Nguyen --- runtime/cudaq/algorithms/evolve.h | 173 ++++++++++-------- runtime/cudaq/dynamics/schedule.cpp | 1 - runtime/nvqir/cudensitymat/cudm_evolution.cpp | 14 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 7 +- runtime/nvqir/cudensitymat/cudm_helpers.h | 3 +- unittests/dynamics/test_evolve_api.cpp | 80 ++++++++ 6 files changed, 196 insertions(+), 82 deletions(-) diff --git a/runtime/cudaq/algorithms/evolve.h b/runtime/cudaq/algorithms/evolve.h index 48af61d177..46b5dfd762 100644 --- a/runtime/cudaq/algorithms/evolve.h +++ b/runtime/cudaq/algorithms/evolve.h @@ -157,90 +157,115 @@ evolve_async(std::function evolveFunctor, } } // namespace __internal__ -template , - cudaq::operator_sum> || - std::is_constructible_v< - cudaq::operator_sum, OperatorTy>>> -evolve_result evolve(const OperatorTy &hamiltonian, - const std::map &dimensions, - const Schedule &schedule, const state &initial_state, - std::shared_ptr integrator = {}, - const std::vector &collapse_operators = {}, - const std::vector &observables = {}, - bool store_intermediate_results = false, - std::optional shots_count = std::nullopt) { +inline evolve_result +evolve(const cudaq::operator_sum &hamiltonian, + const std::map &dimensions, const Schedule &schedule, + const state &initial_state, + std::shared_ptr integrator = {}, + const std::vector> + &collapse_operators = {}, + const std::vector> + &observables = {}, + bool store_intermediate_results = false, + std::optional shots_count = std::nullopt) { #if defined(CUDAQ_DYNAMICS_TARGET) - if constexpr (std::is_same_v, - cudaq::operator_sum>) { - std::vector *> collapseOpsPtr; - for (const auto &cOp : collapse_operators) { - collapseOpsPtr.emplace_back( - const_cast *>(&cOp)); - } - std::vector *> observeOpsPtr; - for (const auto &obsOp : observables) { - observeOpsPtr.emplace_back( - const_cast *>(&obsOp)); - } - // FIXME: change signature of `evolve_single` so that we don't need to - // create the list of pointers. - return evolve_single(hamiltonian, dimensions, schedule, initial_state, - *integrator, collapseOpsPtr, observeOpsPtr, - store_intermediate_results); - } else { - std::vector> - convertedCollapseOps; - for (const auto &cOp : collapse_operators) { - convertedCollapseOps.emplace_back(cOp); - } - std::vector> - convertedObserveOps; - for (const auto &obsOp : observables) { - convertedObserveOps.emplace_back(obsOp); - } - std::vector *> collapseOpsPtr; - for (const auto &cOp : convertedCollapseOps) { - collapseOpsPtr.emplace_back( - const_cast *>(&cOp)); - } - std::vector *> observeOpsPtr; - for (const auto &obsOp : convertedObserveOps) { - observeOpsPtr.emplace_back( - const_cast *>(&obsOp)); - } - return evolve_single(hamiltonian, dimensions, schedule, initial_state, - *integrator, collapseOpsPtr, observeOpsPtr, - store_intermediate_results); + + std::vector *> collapseOpsPtr; + for (const auto &cOp : collapse_operators) { + collapseOpsPtr.emplace_back( + const_cast *>(&cOp)); + } + std::vector *> observeOpsPtr; + for (const auto &obsOp : observables) { + observeOpsPtr.emplace_back( + const_cast *>(&obsOp)); } + // FIXME: change signature of `evolve_single` so that we don't need to + // create the list of pointers. + return evolve_single(hamiltonian, dimensions, schedule, initial_state, + *integrator, collapseOpsPtr, observeOpsPtr, + store_intermediate_results); + #else throw std::runtime_error( "cudaq::evolve is only supported on the 'dynamics' target. Please " "recompile your application with '--target dynamics' flag."); #endif } -template , - cudaq::operator_sum> || - std::is_constructible_v< - cudaq::operator_sum, OperatorTy>>> -// Multiple input state -std::vector -evolve(const OperatorTy &hamiltonian, const std::map &dimensions, - const Schedule &schedule, const std::vector &initial_states, + +inline evolve_result +evolve(const cudaq::product_operator &hamiltonian, + const std::map &dimensions, const Schedule &schedule, + const state &initial_state, std::shared_ptr integrator = {}, - const std::vector &collapse_operators = {}, - const std::vector &observables = {}, + const std::vector> + &collapse_operators = {}, + const std::vector> + &observables = {}, bool store_intermediate_results = false, std::optional shots_count = std::nullopt) { - std::vector results; - for (const auto &initial_state : initial_states) - results.emplace_back(evolve(hamiltonian, dimensions, schedule, - initial_states, integrator, collapse_operators, - observables, store_intermediate_results, - shots_count)); - return results; + std::vector> convertedCollapseOps; + for (const auto &cOp : collapse_operators) { + convertedCollapseOps.emplace_back(cOp); + } + std::vector> convertedObserveOps; + for (const auto &obsOp : observables) { + convertedObserveOps.emplace_back(obsOp); + } + cudaq::operator_sum convertedHam(hamiltonian); + return evolve(convertedHam, dimensions, schedule, initial_state, integrator, + convertedCollapseOps, convertedObserveOps, + store_intermediate_results, shots_count); } + +inline evolve_result +evolve(const cudaq::product_operator &hamiltonian, + const std::map &dimensions, const Schedule &schedule, + const state &initial_state, + std::shared_ptr integrator = {}, + const std::vector> + &collapse_operators = {}, + const std::vector> + &observables = {}, + bool store_intermediate_results = false, + std::optional shots_count = std::nullopt) { + std::vector> + convertedCollapseOps; + for (const auto &cOp : collapse_operators) { + convertedCollapseOps.emplace_back(cOp); + } + std::vector> + convertedObserveOps; + for (const auto &obsOp : observables) { + convertedObserveOps.emplace_back(obsOp); + } + cudaq::product_operator convertedHam(hamiltonian); + return evolve(convertedHam, dimensions, schedule, initial_state, integrator, + convertedCollapseOps, convertedObserveOps, + store_intermediate_results, shots_count); +} + +// template , +// cudaq::operator_sum> || +// std::is_constructible_v< +// cudaq::operator_sum, OperatorTy>>> +// // Multiple input state +// std::vector +// evolve(const OperatorTy &hamiltonian, const std::map &dimensions, +// const Schedule &schedule, const std::vector &initial_states, +// std::shared_ptr integrator = {}, +// const std::vector &collapse_operators = {}, +// const std::vector &observables = {}, +// bool store_intermediate_results = false, +// std::optional shots_count = std::nullopt) { +// std::vector results; +// for (const auto &initial_state : initial_states) +// results.emplace_back(evolve(hamiltonian, dimensions, schedule, +// initial_states, integrator, +// collapse_operators, observables, +// store_intermediate_results, shots_count)); +// return results; +// } } // namespace cudaq diff --git a/runtime/cudaq/dynamics/schedule.cpp b/runtime/cudaq/dynamics/schedule.cpp index a5d2dbc4d9..85773e8017 100644 --- a/runtime/cudaq/dynamics/schedule.cpp +++ b/runtime/cudaq/dynamics/schedule.cpp @@ -20,7 +20,6 @@ Schedule::Schedule( value_function) : _steps(steps), _parameters(parameters), _value_function(value_function) { if (!_value_function) { - printf("HEY\n"); _value_function = [&](const std::string ¶mName, double value) -> std::complex { if (std::find(_parameters.begin(), _parameters.end(), paramName) == diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 46eaf7e834..555a6ad66f 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -21,7 +21,7 @@ namespace cudaq { evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, - const state &initial_state, BaseIntegrator &in_integrator, + const state &initialState, BaseIntegrator &in_integrator, const std::vector *> &collapse_operators, const std::vector *> &observables, @@ -38,8 +38,16 @@ evolve_result evolve_single( throw std::runtime_error("Invalid state."); return castSimState; }; - asCudmState(const_cast(initial_state)) - ->initialize_cudm(handle, dims); + + auto *cudmState = asCudmState(const_cast(initialState)); + cudmState->initialize_cudm(handle, dims); + + state initial_state = [&]() { + if (!collapse_operators.empty() && !cudmState->is_density_matrix()) { + return state(new CuDensityMatState(cudmState->to_density_matrix())); + } + return initialState; + }(); runge_kutta &integrator = dynamic_cast(in_integrator); SystemDynamics system; diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index d98c34ccc8..4b9560cd77 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -363,7 +363,8 @@ void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, std::pair cudm_helper::compute_lindblad_operator_terms( operator_sum &collapseOp, - const std::vector &mode_extents) { + const std::vector &mode_extents, + const std::unordered_map> ¶meters) { std::unordered_map dimensions; for (int i = 0; i < mode_extents.size(); ++i) dimensions[i] = mode_extents[i]; @@ -726,8 +727,8 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( // Handle collapsed operators for (auto &collapse_operators : collapse_operators) { - auto [d1Term, d2Term] = - compute_lindblad_operator_terms(*collapse_operators, mode_extents); + auto [d1Term, d2Term] = compute_lindblad_operator_terms( + *collapse_operators, mode_extents, parameters); HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( handle, liouvillian, diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index 4476b9a4bc..28cfd26e86 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -68,7 +68,8 @@ class cudm_helper { std::pair compute_lindblad_operator_terms( operator_sum &collapseOp, - const std::vector &mode_extents); + const std::vector &mode_extents, + const std::unordered_map> ¶meters); // Helper Functions std::unordered_map diff --git a/unittests/dynamics/test_evolve_api.cpp b/unittests/dynamics/test_evolve_api.cpp index f008024afb..a63c139c3f 100644 --- a/unittests/dynamics/test_evolve_api.cpp +++ b/unittests/dynamics/test_evolve_api.cpp @@ -46,3 +46,83 @@ TEST(EvolveAPITester, checkSimple) { EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); } } + +TEST(EvolveAPITester, checkCavityModel) { + constexpr int N = 10; + constexpr int numSteps = 101; + const auto steps = cudaq::linspace(0, 10, numSteps); + cudaq::Schedule schedule(steps, {"t"}); + auto hamiltonian = cudaq::boson_operator::number(0); + const std::map dimensions{{0, N}}; + std::vector> psi0_(N, 0.0); + psi0_.back() = 1.0; + auto psi0 = cudaq::state::from_data(psi0_); + constexpr double decay_rate = 0.1; + auto collapseOperator = + std::sqrt(decay_rate) * cudaq::boson_operator::annihilate(0); + auto integrator = std::make_shared(); + integrator->dt = 0.01; + auto result = + cudaq::evolve(hamiltonian, dimensions, schedule, psi0, integrator, + {collapseOperator}, {hamiltonian}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + for (const auto &t : schedule) { + const double expected = (N - 1) * std::exp(-decay_rate * t); + theoryResults.emplace_back(expected); + } + + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } +} + +// TEST(EvolveAPITester, checkTimeDependent) { +// constexpr int N = 10; +// constexpr int numSteps = 101; +// const auto steps = cudaq::linspace(0, 10, numSteps); +// cudaq::Schedule schedule(steps, {"t"}); +// auto hamiltonian = cudaq::boson_operator::number(0); +// const std::map dimensions{{0, N}}; +// std::vector> psi0_(N, 0.0); +// psi0_.back() = 1.0; +// auto psi0 = cudaq::state::from_data(psi0_); +// constexpr double decay_rate = 0.1; + +// auto td_function = +// [decay_rate](const std::unordered_map> +// ¶meters) { +// auto entry = parameters.find("t"); +// if (entry == parameters.end()) +// throw std::runtime_error("Cannot find value of expected parameter"); +// const auto t = entry->second.real(); +// return std::sqrt(decay_rate * std::exp(-t)); +// }; + +// auto collapseOperator = cudaq::scalar_operator(td_function) * +// cudaq::boson_operator::annihilate(0); +// auto integrator = std::make_shared(); +// integrator->dt = 0.01; +// auto result = +// cudaq::evolve(hamiltonian, dimensions, schedule, psi0, integrator, +// {collapseOperator}, {hamiltonian}, true); +// EXPECT_TRUE(result.get_expectation_values().has_value()); +// EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); +// std::vector theoryResults; +// for (const auto &t : schedule) { +// const double expected = +// (N - 1) * std::exp(-decay_rate * (1.0 - std::exp(-t)) * t); +// theoryResults.emplace_back(expected); +// } + +// int count = 0; +// for (auto expVals : result.get_expectation_values().value()) { +// EXPECT_EQ(expVals.size(), 1); +// std::cout << "Result = " << (double)expVals[0] << "; expected " +// << theoryResults[count] << "\n"; +// EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); +// } +// } From 1b2eecc308cfc7c685ae1ffe292a7de7dc1abe76 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 18 Feb 2025 21:48:31 -0800 Subject: [PATCH 291/311] Removing batch_size from tensor_callback and formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics_integrators.h | 3 ++- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 27 ++++++++++--------- .../cudensitymat/runge_kutta_integrator.cpp | 8 +++--- unittests/dynamics/test_evolve_api.cpp | 6 +++-- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/runtime/cudaq/dynamics_integrators.h b/runtime/cudaq/dynamics_integrators.h index 07e4164e8e..0d6d07771b 100644 --- a/runtime/cudaq/dynamics_integrators.h +++ b/runtime/cudaq/dynamics_integrators.h @@ -32,7 +32,8 @@ class runge_kutta : public BaseIntegrator { void integrate(double target_time) override; void set_state(cudaq::state initial_state, double t0) override; std::pair get_state() override; - void set_system(const SystemDynamics &system, const cudaq::Schedule& schedule); + void set_system(const SystemDynamics &system, + const cudaq::Schedule &schedule); private: double m_t; diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 4b9560cd77..0239274a41 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -111,28 +111,28 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op, using WrapperFuncType = int32_t (*)( cudensitymatTensorCallback_t, cudensitymatElementaryOperatorSparsity_t, - int32_t, const int64_t[], const int32_t[], double, int64_t, int32_t, + int32_t, const int64_t[], const int32_t[], double, int32_t, const double[], cudaDataType_t, void *, cudaStream_t); auto wrapper = [](cudensitymatTensorCallback_t callback, cudensitymatElementaryOperatorSparsity_t sparsity, int32_t num_modes, const int64_t mode_extents[], const int32_t diagonal_offsets[], double time, - int64_t batch_size, int32_t num_params, - const double params[], cudaDataType_t data_type, - void *tensor_storage, cudaStream_t stream) -> int32_t { + int32_t num_params, const double params[], + cudaDataType_t data_type, void *tensor_storage, + cudaStream_t stream) -> int32_t { try { auto *context = reinterpret_cast(callback); matrix_operator &stored_op = context->tensorOp; - // if (num_params != 2 * context->paramNames.size()) - // throw std::runtime_error( - // fmt::format("[Internal Error] Invalid number of tensor callback " - // "parameters. Expected {} double values " - // "representing {} complex parameters but received - // {}.", std::to_string(2 * context->paramNames.size()), - // std::to_string(context->paramNames.size()), - // std::to_string(num_params))); + if (num_params != 2 * context->paramNames.size()) + throw std::runtime_error( + fmt::format("[Internal Error] Invalid number of tensor callback " + "parameters. Expected {} double values " + "representing {} complex parameters but received + {}.", std::to_string(2 * context->paramNames.size()), + std::to_string(context->paramNames.size()), + std::to_string(num_params))); std::unordered_map> param_map; for (size_t i = 0; i < context->paramNames.size(); ++i) { @@ -694,7 +694,8 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( parameters.begin(), parameters.end()); auto ks = std::views::keys(sortedParameters); const std::vector keys{ks.begin(), ks.end()}; - for (auto &[coeff, term] : convert_to_cudensitymat(op, parameters, mode_extents)) { + for (auto &[coeff, term] : + convert_to_cudensitymat(op, parameters, mode_extents)) { cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; if (coeff.is_constant()) { const auto coeffVal = coeff.evaluate(); diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index e4823e23af..acba052dd3 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -58,8 +58,8 @@ void runge_kutta::integrate(double target_time) { } auto liouvillian = helper.construct_liouvillian( - *m_system.hamiltonian, m_system.collapseOps, m_system.modeExtents, params, - castSimState.is_density_matrix()); + *m_system.hamiltonian, m_system.collapseOps, m_system.modeExtents, + params, castSimState.is_density_matrix()); m_stepper = std::make_unique(castSimState.get_handle(), liouvillian); } @@ -95,8 +95,8 @@ void runge_kutta::integrate(double target_time) { params[param] = m_schedule.value_function()(param, m_t + step_size / 2.0); } - auto k2State = - m_stepper->compute(*m_state, m_t + step_size / 2.0, step_size, params); + auto k2State = m_stepper->compute(*m_state, m_t + step_size / 2.0, + step_size, params); auto &k2 = *asCudmState(k2State); k2 *= (step_size / 2.0); diff --git a/unittests/dynamics/test_evolve_api.cpp b/unittests/dynamics/test_evolve_api.cpp index a63c139c3f..eaeeb26b3a 100644 --- a/unittests/dynamics/test_evolve_api.cpp +++ b/unittests/dynamics/test_evolve_api.cpp @@ -93,11 +93,13 @@ TEST(EvolveAPITester, checkCavityModel) { // constexpr double decay_rate = 0.1; // auto td_function = -// [decay_rate](const std::unordered_map> +// [decay_rate](const std::unordered_map> // ¶meters) { // auto entry = parameters.find("t"); // if (entry == parameters.end()) -// throw std::runtime_error("Cannot find value of expected parameter"); +// throw std::runtime_error("Cannot find value of expected +// parameter"); // const auto t = entry->second.real(); // return std::sqrt(decay_rate * std::exp(-t)); // }; From d684a15eb77088edb2ccfdc4e7d50d976aa9b468 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 18 Feb 2025 22:21:30 -0800 Subject: [PATCH 292/311] Fixing unittest and string formatting Signed-off-by: Sachin Pisal --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 5 +++-- unittests/dynamics/test_cudm_time_stepper.cpp | 16 ++++++++++------ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 0239274a41..4f6389ced8 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -129,8 +129,9 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op, throw std::runtime_error( fmt::format("[Internal Error] Invalid number of tensor callback " "parameters. Expected {} double values " - "representing {} complex parameters but received - {}.", std::to_string(2 * context->paramNames.size()), + "representing {} complex parameters but received " + "{}.", + std::to_string(2 * context->paramNames.size()), std::to_string(context->paramNames.size()), std::to_string(num_params))); diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 285bb7bcdd..9409339a2c 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -211,9 +211,9 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { } TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { - const std::vector> initialState = { - {1.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}; - const std::vector dims = {4}; + const std::vector> initialState = {{1.0, 0.0}, + {1.0, 0.0}}; + const std::vector dims = {2}; auto inputState = cudaq::state::from_data(initialState); auto *simState = cudaq::state_helper::getSimulationState(&inputState); auto *castSimState = dynamic_cast(simState); @@ -229,6 +229,10 @@ TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { [paramName](const std::vector &dimensions, const std::unordered_map> ¶meters) -> matrix_2 { + if (dimensions.empty()) { + throw std::runtime_error("Empty dimensions vector received!"); + } + auto entry = parameters.find(paramName); if (entry == parameters.end()) throw std::runtime_error( @@ -243,7 +247,7 @@ TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { return mat; }; - matrix_operator::define("CustomTensorOp", {-1}, tensorFunction); + matrix_operator::define("CustomTensorOp", {2}, tensorFunction); auto op = cudaq::matrix_operator::instantiate("CustomTensorOp", {0}); auto cudmOp = helper_->convert_to_cudensitymat_operator( @@ -252,11 +256,11 @@ TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { auto time_stepper = std::make_unique(handle_, cudmOp); auto outputState = time_stepper->compute(inputState, 1.0, 1.0, params); outputState.dump(std::cout); - std::vector> outputStateVec(4); + std::vector> outputStateVec(2); outputState.to_host(outputStateVec.data(), outputStateVec.size()); // Create operator move the state up 1 step. const std::vector> expectedOutputState = { - paramValue, {0.0, 0.0}, {0.0, 0.0}, std::conj(paramValue)}; + paramValue, std::conj(paramValue)}; for (std::size_t i = 0; i < expectedOutputState.size(); ++i) { EXPECT_TRUE(std::abs(expectedOutputState[i] - outputStateVec[i]) < 1e-12); From fb0e146722d396435916417f52377b9270142ede Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Tue, 18 Feb 2025 23:43:01 -0800 Subject: [PATCH 293/311] Fixing checkScalarTd Signed-off-by: Sachin Pisal --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 4f6389ced8..23c5bd5633 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -125,6 +125,11 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op, auto *context = reinterpret_cast(callback); matrix_operator &stored_op = context->tensorOp; + if (num_modes <= 0) { + std::cerr << "num_modes is invalid: " << num_modes << std::endl; + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + if (num_params != 2 * context->paramNames.size()) throw std::runtime_error( fmt::format("[Internal Error] Invalid number of tensor callback " @@ -143,13 +148,24 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op, context->paramNames[i], param_map[context->paramNames[i]]); } - std::unordered_map dimensions = {}; + std::unordered_map dimensions; + for (int i = 0; i < num_modes; ++i) { + dimensions[i] = static_cast(mode_extents[i]); + } + + if (dimensions.empty()) { + std::cerr << "Dimension map is empty!" << std::endl; + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + matrix_2 matrix_data = stored_op.to_matrix(dimensions, param_map); std::size_t rows = matrix_data.get_rows(); std::size_t cols = matrix_data.get_columns(); - if (num_modes != rows) { + if (rows != cols) { + std::cerr << "Non-square matrix encountered: " << rows << "x" << cols + << std::endl; return CUDENSITYMAT_STATUS_INVALID_VALUE; } @@ -171,6 +187,7 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op, } } } else { + std::cerr << "Invalid CUDA data type: " << data_type << std::endl; return CUDENSITYMAT_STATUS_INVALID_VALUE; } From c1a3b2c83bff9a1966e158f3d0dacc20ab0502fe Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 19 Feb 2025 00:04:43 -0800 Subject: [PATCH 294/311] Fixing CheckScalarCallback by enabling memcpy to copy the flattened matrix Signed-off-by: Sachin Pisal --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 26 ++++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 23c5bd5633..cd121a2501 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -169,23 +169,21 @@ cudm_helper::_wrap_tensor_callback(const matrix_operator &op, return CUDENSITYMAT_STATUS_INVALID_VALUE; } + std::vector> flat_matrix = + flatten_matrix(matrix_data); + if (data_type == CUDA_C_64F) { - auto *storage = static_cast(tensor_storage); - for (size_t i = 0; i < rows; i++) { - for (size_t j = 0; j < cols; j++) { - storage[i * cols + j] = make_cuDoubleComplex( - matrix_data[{i, j}].real(), matrix_data[{i, j}].imag()); - } - } + memcpy(tensor_storage, flat_matrix.data(), + flat_matrix.size() * sizeof(cuDoubleComplex)); } else if (data_type == CUDA_C_32F) { - auto *storage = static_cast(tensor_storage); - for (size_t i = 0; i < rows; i++) { - for (size_t j = 0; j < cols; j++) { - storage[i * cols + j] = make_cuFloatComplex( - static_cast(matrix_data[{i, j}].real()), - static_cast(matrix_data[{i, j}].imag())); - } + std::vector flat_matrix_float(flat_matrix.size()); + for (size_t i = 0; i < flat_matrix.size(); i++) { + flat_matrix_float[i] = + make_cuFloatComplex(static_cast(flat_matrix[i].real()), + static_cast(flat_matrix[i].imag())); } + memcpy(tensor_storage, flat_matrix_float.data(), + flat_matrix_float.size() * sizeof(cuFloatComplex)); } else { std::cerr << "Invalid CUDA data type: " << data_type << std::endl; return CUDENSITYMAT_STATUS_INVALID_VALUE; From 7efb20c1c7e3b2ad547892fca2ffcb9632698ba4 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 19 Feb 2025 16:31:00 -0800 Subject: [PATCH 295/311] Adding cavity_qed example for C++ dynamics Signed-off-by: Sachin Pisal --- .../examples/cpp/dynamics/cavity_qed.cpp | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 docs/sphinx/examples/cpp/dynamics/cavity_qed.cpp diff --git a/docs/sphinx/examples/cpp/dynamics/cavity_qed.cpp b/docs/sphinx/examples/cpp/dynamics/cavity_qed.cpp new file mode 100644 index 0000000000..9228514393 --- /dev/null +++ b/docs/sphinx/examples/cpp/dynamics/cavity_qed.cpp @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/algorithms/evolve.h" +#include "cudaq/dynamics_integrators.h" +#include "cudaq/evolution.h" +#include "cudaq/operators.h" +#include "cudaq/schedule.h" +#include "matplotlibcpp.h" +#include + +namespace plt = matplotlibcpp; + +int main() { + + cudaq::set_target_backend("dynamics"); + + // System dimensions: + // subsystem 0 (atom) has 2 levels + // subsystem 1 (cavity) has 10 levels + std::map dimensions{{0, 2}, {1, 10}}; + + // For the cavity subsystem 1 + auto a = cudaq::matrix_operator::annihilate(1); + auto a_dag = cudaq::matrix_operator::create(1); + + // For the atom subsystem 0 + auto sm = cudaq::matrix_operator::annihilate(0); + auto sm_dag = cudaq::matrix_operator::create(0); + + cudaq::product_operator atom_occ_op_t = + cudaq::matrix_operator::number(0); + cudaq::operator_sum atom_occ_op(atom_occ_op_t); + + cudaq::product_operator cavity_occ_op_t = + cudaq::matrix_operator::number(1); + cudaq::operator_sum cavity_occ_op(cavity_occ_op_t); + + auto hamiltonian = 2 * M_PI * atom_occ_op + 2 * M_PI * cavity_occ_op + + 2 * M_PI * 0.25 * (sm * a_dag + sm_dag * a); + + // Build the initial state + // For the atom, the density matrix in the ground state + cudaq::matrix_2 qubit_state({1.0, 0.0}, {0.0, 0.0}); + + // For the cavity, 1 10x10 matrix with a single photon number state at |5> + cudaq::matrix_2 cavity_state = cudaq::matrix_2(10, 10); + cavity_state[{5, 5}] = 1.0; + + // Compute the tensor (kronecker) product of the atom and cavity states + cudaq::matrix_2 rho = qubit_state.kronecker_inplace(cavity_state); + + // Flatten the matrix + std::vector> flat_rho; + for (size_t j = 0; j < rho.get_columns(); ++j) { + for (size_t i = 0; i < rho.get_rows(); ++i) { + flat_rho.push_back(rho[{i, j}]); + } + } + + cudaq::state_data rho_data = flat_rho; + + // Create a CUDA quantum state from a density matrix + auto rho0 = cudaq::state::from_data(rho_data); + + // Create time steps between 0 and 10 + const int num_steps = 201; + const double t0 = 0.0, t1 = 10.0; + std::vector steps(num_steps); + double dt = (t1 - t0) / (num_steps - 1); + for (int i = 0; i < num_steps; ++i) { + steps[i] = t0 + i * dt; + } + + // Create a schedule for the time steps and a label for the time parameter + std::vector labels = {"time"}; + cudaq::Schedule schedule(steps, labels); + + cudaq::runge_kutta integrator; + integrator.dt = 0.001; + integrator.order = 1; + + // Evolve without collapse operators + cudaq::evolve_result evolve_result = + cudaq::evolve(hamiltonian, dimensions, schedule, rho0, integrator, {}, + {hamiltonian}, true); + + constexpr double decay_rate = 0.1; + auto collapse_operator = std::sqrt(decay_rate) * a; + // Evolve with collapse operators + cudaq::evolve_result evolve_result_decay = + cudaq::evolve(hamiltonian, dimensions, schedule, rho0, integrator, + {collapse_operator}, {hamiltonian}, true); + + // Lambda to extract expectation values for a given observable index + auto get_expectation = [](int idx, + const auto &result) -> std::vector { + std::vector expectations; + + auto all_exps = result.get_expectation_values().value(); + for (const auto &exp_vals : all_exps) { + expectations.push_back((double)exp_vals[idx]); + } + return expectations; + }; + + auto ideal_result0 = get_expectation(0, evolve_result); + auto ideal_result1 = get_expectation(1, evolve_result); + auto decay_result0 = get_expectation(0, evolve_result_decay); + auto decay_result1 = get_expectation(1, evolve_result_decay); + + // Plot the results + plt::figure_size(1000, 600); + + // Subplot 1: No decay + plt::subplot(1, 2, 1); + plt::plot(steps, ideal_result0, {{"label", "Cavity Photon Number"}}); + plt::plot(steps, ideal_result1, {{"label", "Atom Excitation Probability"}}); + plt::xlabel("Time"); + plt::ylabel("Expectation value"); + plt::legend(); + plt::title("No decay"); + + // Subplot 2: With decay + plt::subplot(1, 2, 2); + plt::plot(steps, decay_result0, {{"label", "Cavity Photon Number"}}); + plt::plot(steps, decay_result1, {{"label", "Atom Excitation Probability"}}); + plt::xlabel("Time"); + plt::ylabel("Expectation value"); + plt::legend(); + plt::title("No decay"); + + plt::save("cavity_qed.png"); + + std::cout << "Simulation complete. Plot saved to cavity_qed.png" << std::endl; + return 0; +} \ No newline at end of file From d6120305484050589caf58e910d1074c19bdbaa1 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Wed, 19 Feb 2025 16:31:48 -0800 Subject: [PATCH 296/311] Adding cross_resonance example for C++ dynamics Signed-off-by: Sachin Pisal --- .../examples/cpp/dynamics/cross_resonance.cpp | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 docs/sphinx/examples/cpp/dynamics/cross_resonance.cpp diff --git a/docs/sphinx/examples/cpp/dynamics/cross_resonance.cpp b/docs/sphinx/examples/cpp/dynamics/cross_resonance.cpp new file mode 100644 index 0000000000..84d09eaba9 --- /dev/null +++ b/docs/sphinx/examples/cpp/dynamics/cross_resonance.cpp @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "cudaq/algorithms/evolve.h" +#include "cudaq/dynamics_integrators.h" +#include "cudaq/evolution.h" +#include "cudaq/operators.h" +#include "cudaq/schedule.h" +#include "matplotlibcpp.h" +#include + +namespace plt = matplotlibcpp; + +int main() { + + cudaq::set_target_backend("dynamics"); + + // Detuning between two qubits + double delta = 100 * 2 * M_PI; + // Static coupling between qubits + double J = 7 * 2 * M_PI; + // Spurious electromagnetic crosstalk + double m_12 = 0.2; + // Drive strength + double Omega = 20 * 2 * M_PI; + + auto hamiltonian = + (delta / 2.0) * cudaq::spin_operator::z(0) + + J * (cudaq::spin_operator::minus(1) * cudaq::spin_operator::plus(0) + + cudaq::spin_operator::plus(1) * cudaq::spin_operator::minus(0)) + + Omega * cudaq::spin_operator::x(0) + + m_12 * Omega * cudaq::spin_operator::x(1); + + std::map dimensions{{0, 2}, {1, 2}}; + + // Build the initial state + cudaq::matrix_2 rho_mat({1.0, 0.0}, {0.0, 0.0}); + + // Flatten the matrix + std::vector> flat_rho; + for (size_t j = 0; j < rho_mat.get_columns(); ++j) { + for (size_t i = 0; i < rho_mat.get_rows(); ++i) { + flat_rho.push_back(rho_mat[{i, j}]); + } + } + + cudaq::state_data rho_data = flat_rho; + auto rho0 = cudaq::state::from_data(rho_data); + + // Two initial state vectors for the 2-qubit system (dimension 4) + // psi_00 corresponds to |00> and psi_10 corresponds to |10> + auto psi_00 = cudaq::state::from_data({1.0, 0.0, 0.0, 0.0}); + auto psi_10 = cudaq::state::from_data({0.0, 0.0, 1.0, 0.0}); + + // Create a schedule of time steps + const int num_steps = 1001; + const double t0 = 0.0, t1 = 1.0; + std::vector steps(num_steps); + double dt = (t1 - t0) / (num_steps - 1); + for (int i = 0; i < num_steps; ++i) { + steps[i] = t0 + i * dt; + } + std::vector labels = {"time"}; + cudaq::Schedule schedule(steps, labels); + + cudaq::runge_kutta integrator; + integrator.dt = 0.001; + integrator.order = 1; + + std::vector observables = { + cudaq::spin_operator::x(0), cudaq::spin_operator::y(0), + cudaq::spin_operator::z(0), cudaq::spin_operator::x(1), + cudaq::spin_operator::y(1), cudaq::spin_operator::z(1)}; + + // Evolution for initial state |00> + auto evolution_result_00 = + cudaq::evolve(hamiltonian, dimensions, schedule, psi_00, observables, {}, + true, integrator); + + // Evolution for initial state |10> + auto evolution_result_10 = + cudaq::evolve(hamiltonian, dimensions, schedule, psi_10, observables, {}, + true, integrator); + + auto get_result = [](int idx, const auto &result) -> std::vector { + std::vector expectations; + auto all_exps = result.get_expectation_values().value(); + for (const auto &exp_vals : all_exps) { + expectations.push_back((double)exp_vals[idx]); + } + return expectations; + }; + + // For the two evolutions, extract the six observable trajectories. + auto result_00_0 = get_result(0, evolution_result_00); + auto result_00_1 = get_result(1, evolution_result_00); + auto result_00_2 = get_result(2, evolution_result_00); + auto result_00_3 = get_result(3, evolution_result_00); + auto result_00_4 = get_result(4, evolution_result_00); + auto result_00_5 = get_result(5, evolution_result_00); + + auto result_10_0 = get_result(0, evolution_result_10); + auto result_10_1 = get_result(1, evolution_result_10); + auto result_10_2 = get_result(2, evolution_result_10); + auto result_10_3 = get_result(3, evolution_result_10); + auto result_10_4 = get_result(4, evolution_result_10); + auto result_10_5 = get_result(5, evolution_result_10); + + // Plot the results + plt::figure_size(1000, 600); + + // Subplot 1: Expectation value for qubit 1. + plt::subplot(1, 2, 1); + plt::plot(steps, result_00_5, {{"label", "$|\\psi_0\\rangle=|00\\rangle$"}}); + plt::plot(steps, result_10_5, {{"label", "$|\\psi_0\\rangle=|10\\rangle$"}}); + plt::xlabel("Time"); + plt::ylabel("$\\langle Z_2 \\rangle$"); + plt::legend(); + + // Subplot 2: Expectation value for qubit 1. + plt::subplot(1, 2, 2); + plt::plot(steps, result_00_4, {{"label", "$|\\psi_0\\rangle=|00\\rangle$"}}); + plt::plot(steps, result_10_4, {{"label", "$|\\psi_0\\rangle=|10\\rangle$"}}); + plt::xlabel("Time"); + plt::ylabel("$\\langle Y_2 \\rangle$"); + plt::legend(); + + plt::save("cross_resonance.png"); + + std::cout << "Simulation complete. Plot saved to cross_resonance.png" + << std::endl; + return 0; +} \ No newline at end of file From 677f4602e3b4c26159cc78dcf8fdebc1009b27fb Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 20 Feb 2025 01:43:25 +0000 Subject: [PATCH 297/311] Hook callback support to Linblad conversion Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 260 +++++++++++++++--- runtime/nvqir/cudensitymat/cudm_helpers.h | 6 +- unittests/dynamics/test_cudm_time_stepper.cpp | 8 +- unittests/dynamics/test_evolve_api.cpp | 89 +++--- 4 files changed, 280 insertions(+), 83 deletions(-) diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index cd121a2501..8c3ea4369d 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -376,11 +376,218 @@ void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); } -std::pair -cudm_helper::compute_lindblad_operator_terms( +static product_operator +computeDagger(const cudaq::matrix_operator &op) { + const std::string daggerOpName = op.to_string(false) + "_dagger"; + try { + auto func = [op](const std::vector &dimensions, + const std::unordered_map> + ¶ms) { + std::unordered_map dims; + if (dimensions.size() != op.degrees().size()) + throw std::runtime_error("Dimension mismatched"); + + for (int i = 0; i < dimensions.size(); ++i) { + dims[op.degrees()[i]] = dimensions[i]; + } + auto originalMat = op.to_matrix(dims, params); + return matrix_2::adjoint(originalMat); + }; + matrix_operator::define(daggerOpName, {-1}, std::move(func)); + } catch (...) { + // Nothing, this has been define + } + return matrix_operator::instantiate(daggerOpName, op.degrees()); +} + +static scalar_operator computeDagger(const scalar_operator &scalar) { + if (scalar.is_constant()) { + return scalar_operator(std::conj(scalar.evaluate())); + } else { + return scalar_operator( + [scalar]( + const std::unordered_map> ¶ms) + -> std::complex { + return std::conj(scalar.evaluate(params)); + }); + } +} + +static product_operator +computeDagger(const product_operator &productOp) { + std::vector> daggerOps; + for (const auto &component : productOp.get_terms()) { + if (const auto *elem_op = + dynamic_cast(&component)) { + daggerOps.emplace_back(computeDagger(*elem_op)); + } else { + throw std::runtime_error("Unhandled type!"); + } + } + std::reverse(daggerOps.begin(), daggerOps.end()); + + if (daggerOps.empty()) { + throw std::runtime_error("Empty product operator"); + } + product_operator daggerProduct = daggerOps[0]; + for (std::size_t i = 1; i < daggerOps.size(); ++i) { + daggerProduct *= daggerOps[i]; + } + daggerProduct *= computeDagger(productOp.get_coefficient()); + return daggerProduct; +} + +static operator_sum +computeDagger(const operator_sum &sumOp) { + + const auto &productTerms = sumOp.get_terms(); + + if (productTerms.empty()) { + throw std::runtime_error("Empty operator sum"); + } + product_operator firstDaggerTerm = + computeDagger(productTerms[0]); + operator_sum daggerOpSum(firstDaggerTerm); + + + for (std::size_t i = 1; i < productTerms.size(); ++i) { + daggerOpSum += computeDagger(productTerms[i]); + } + + return daggerOpSum; +} + +std::vector> +cudm_helper::compute_lindblad_terms( operator_sum &collapseOp, const std::vector &mode_extents, const std::unordered_map> ¶meters) { + std::vector> + lindbladTerms; + for (const product_operator &l_op : collapseOp.get_terms()) { + for (const product_operator &r_op : + collapseOp.get_terms()) { + scalar_operator coeff = + l_op.get_coefficient() * computeDagger(r_op.get_coefficient()); + auto ldag = computeDagger(r_op); + { + // L * rho * L_dag + std::vector elem_ops; + std::vector> all_degrees; + std::vector> all_action_dual_modalities; + + for (const auto &component : l_op.get_terms()) { + if (const auto *elem_op = + dynamic_cast(&component)) { + auto cudm_elem_op = + create_elementary_operator(*elem_op, parameters, mode_extents); + elem_ops.emplace_back(cudm_elem_op); + all_degrees.emplace_back(elem_op->degrees()); + all_action_dual_modalities.emplace_back( + std::vector(elem_op->degrees().size(), 0)); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + + for (const auto &component : ldag.get_terms()) { + if (const auto *elem_op = + dynamic_cast(&component)) { + auto cudm_elem_op = + create_elementary_operator(*elem_op, parameters, mode_extents); + elem_ops.emplace_back(cudm_elem_op); + all_degrees.emplace_back(elem_op->degrees()); + all_action_dual_modalities.emplace_back( + std::vector(elem_op->degrees().size(), 1)); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + + cudensitymatOperatorTerm_t D1_term; + // Create an empty operator term + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D1_term)); // the created empty operator term + m_operatorTerms.emplace(D1_term); + + append_elementary_operator_to_term(D1_term, elem_ops, all_degrees, + all_action_dual_modalities); + lindbladTerms.emplace_back(std::make_pair(coeff, D1_term)); + } + + product_operator L_daggerTimesL = -0.5 * ldag * l_op; + { + std::vector elem_ops; + std::vector> all_degrees; + std::vector> all_action_dual_modalities_left; + std::vector> all_action_dual_modalities_right; + for (const auto &component : L_daggerTimesL.get_terms()) { + if (const auto *elem_op = + dynamic_cast(&component)) { + auto cudm_elem_op = + create_elementary_operator(*elem_op, parameters, mode_extents); + elem_ops.emplace_back(cudm_elem_op); + all_degrees.emplace_back(elem_op->degrees()); + all_action_dual_modalities_left.emplace_back( + std::vector(elem_op->degrees().size(), 0)); + all_action_dual_modalities_right.emplace_back( + std::vector(elem_op->degrees().size(), 1)); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + { + cudensitymatOperatorTerm_t D2_term; + // Create an empty operator term + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D2_term)); // the created empty operator term + m_operatorTerms.emplace(D2_term); + // For left side, we need to reverse the order + std::vector d2Ops(elem_ops); + std::reverse(d2Ops.begin(), d2Ops.end()); + std::vector> d2Degrees(all_degrees); + std::reverse(d2Degrees.begin(), d2Degrees.end()); + append_elementary_operator_to_term(D2_term, d2Ops, d2Degrees, + all_action_dual_modalities_left); + lindbladTerms.emplace_back( + std::make_pair(L_daggerTimesL.get_coefficient(), D2_term)); + } + { + cudensitymatOperatorTerm_t D3_term; + // Create an empty operator term + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + handle, + mode_extents.size(), // Hilbert space rank (number of dimensions) + mode_extents.data(), // Hilbert space shape + &D3_term)); // the created empty operator term + m_operatorTerms.emplace(D3_term); + + append_elementary_operator_to_term(D3_term, elem_ops, all_degrees, + all_action_dual_modalities_right); + lindbladTerms.emplace_back( + std::make_pair(L_daggerTimesL.get_coefficient(), D3_term)); + } + } + } + } + return lindbladTerms; +} + +std::pair cudm_helper:: + compute_lindblad_operator_terms( + operator_sum &collapseOp, + const std::vector &mode_extents, + const std::unordered_map> + ¶meters) { std::unordered_map dimensions; for (int i = 0; i < mode_extents.size(); ++i) dimensions[i] = mode_extents[i]; @@ -744,38 +951,23 @@ cudensitymatOperator_t cudm_helper::construct_liouvillian( // Handle collapsed operators for (auto &collapse_operators : collapse_operators) { - auto [d1Term, d2Term] = compute_lindblad_operator_terms( - *collapse_operators, mode_extents, parameters); - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, - d1Term, // appended operator term - 0, // operator term action duality as a whole (no duality reversing in - // this case) - make_cuDoubleComplex(1, 0.0), // constant coefficient associated with - // the operator term as a whole - {nullptr, - nullptr})); // no time-dependent coefficient associated with the - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, - d2Term, // appended operator term - 0, // operator term action duality as a whole (no duality reversing in - // this case) - make_cuDoubleComplex(1, 0.0), // constant coefficient associated with - // the operator term as a whole - {nullptr, - nullptr})); // no time-dependent coefficient associated with the - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, - d2Term, // appended operator term - 1, // operator term action duality as a whole (no duality reversing in - // this case) - make_cuDoubleComplex(1, 0.0), // constant coefficient associated with - // the operator term as a whole - {nullptr, - nullptr})); // no time-dependent coefficient associated with the + for (auto &[coeff, term] : compute_lindblad_terms( + *collapse_operators, mode_extents, parameters)) { + cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, + nullptr}; + if (coeff.is_constant()) { + const auto coeffVal = coeff.evaluate(); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, term, 0, + make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), + wrapped_callback)); + } else { + wrapped_callback = _wrap_callback(coeff, keys); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + handle, liouvillian, term, 0, make_cuDoubleComplex(1.0, 0.0), + wrapped_callback)); + } + } } return liouvillian; diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h index 28cfd26e86..f109a703c9 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ b/runtime/nvqir/cudensitymat/cudm_helpers.h @@ -44,7 +44,11 @@ class cudm_helper { const std::unordered_map> ¶meters, const operator_sum &op, const std::vector &mode_extents); - + std::vector> + compute_lindblad_terms( + operator_sum &collapseOp, + const std::vector &mode_extents, + const std::unordered_map> ¶meters); std::vector> convert_to_cudensitymat( const operator_sum &op, diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 9409339a2c..b4fa23cbaf 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -142,10 +142,12 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { auto *castSimState = dynamic_cast(simState); EXPECT_TRUE(castSimState != nullptr); castSimState->initialize_cudm(handle_, dims); - - auto c_op_0 = cudaq::matrix_operator::annihilate(0); + cudaq::product_operator c_op_0 = + cudaq::matrix_operator::annihilate(0); + cudaq::operator_sum c_op(c_op_0); + cudaq::operator_sum zero_op = 0.0 * c_op; auto cudm_lindblad_op = - helper_->compute_lindblad_operator({c_op_0.to_matrix({{0, 10}})}, dims); + helper_->construct_liouvillian(zero_op, {&c_op}, dims, {}, true); auto time_stepper = std::make_unique(handle_, cudm_lindblad_op); auto output_state = time_stepper->compute(input_state, 0.0, 1.0, {}); diff --git a/unittests/dynamics/test_evolve_api.cpp b/unittests/dynamics/test_evolve_api.cpp index eaeeb26b3a..077a7b3076 100644 --- a/unittests/dynamics/test_evolve_api.cpp +++ b/unittests/dynamics/test_evolve_api.cpp @@ -80,51 +80,50 @@ TEST(EvolveAPITester, checkCavityModel) { } } -// TEST(EvolveAPITester, checkTimeDependent) { -// constexpr int N = 10; -// constexpr int numSteps = 101; -// const auto steps = cudaq::linspace(0, 10, numSteps); -// cudaq::Schedule schedule(steps, {"t"}); -// auto hamiltonian = cudaq::boson_operator::number(0); -// const std::map dimensions{{0, N}}; -// std::vector> psi0_(N, 0.0); -// psi0_.back() = 1.0; -// auto psi0 = cudaq::state::from_data(psi0_); -// constexpr double decay_rate = 0.1; +TEST(EvolveAPITester, checkTimeDependent) { + constexpr int N = 10; + constexpr int numSteps = 101; + const auto steps = cudaq::linspace(0, 10, numSteps); + cudaq::Schedule schedule(steps, {"t"}); + auto hamiltonian = cudaq::boson_operator::number(0); + const std::map dimensions{{0, N}}; + std::vector> psi0_(N, 0.0); + psi0_.back() = 1.0; + auto psi0 = cudaq::state::from_data(psi0_); + constexpr double decay_rate = 0.1; -// auto td_function = -// [decay_rate](const std::unordered_map> -// ¶meters) { -// auto entry = parameters.find("t"); -// if (entry == parameters.end()) -// throw std::runtime_error("Cannot find value of expected -// parameter"); -// const auto t = entry->second.real(); -// return std::sqrt(decay_rate * std::exp(-t)); -// }; + auto td_function = + [decay_rate](const std::unordered_map> + ¶meters) { + auto entry = parameters.find("t"); + if (entry == parameters.end()) + throw std::runtime_error("Cannot find value of expected parameter"); + const auto t = entry->second.real(); + const auto result = std::sqrt(decay_rate * std::exp(-t)); + return result; + }; -// auto collapseOperator = cudaq::scalar_operator(td_function) * -// cudaq::boson_operator::annihilate(0); -// auto integrator = std::make_shared(); -// integrator->dt = 0.01; -// auto result = -// cudaq::evolve(hamiltonian, dimensions, schedule, psi0, integrator, -// {collapseOperator}, {hamiltonian}, true); -// EXPECT_TRUE(result.get_expectation_values().has_value()); -// EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); -// std::vector theoryResults; -// for (const auto &t : schedule) { -// const double expected = -// (N - 1) * std::exp(-decay_rate * (1.0 - std::exp(-t)) * t); -// theoryResults.emplace_back(expected); -// } + auto collapseOperator = cudaq::scalar_operator(td_function) * + cudaq::boson_operator::annihilate(0); + auto integrator = std::make_shared(); + integrator->dt = 0.01; + auto result = + cudaq::evolve(hamiltonian, dimensions, schedule, psi0, integrator, + {collapseOperator}, {hamiltonian}, true); + EXPECT_TRUE(result.get_expectation_values().has_value()); + EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); + std::vector theoryResults; + for (const auto &t : schedule) { + const double expected = + (N - 1) * std::exp(-decay_rate * (1.0 - std::exp(-t))); + theoryResults.emplace_back(expected); + } -// int count = 0; -// for (auto expVals : result.get_expectation_values().value()) { -// EXPECT_EQ(expVals.size(), 1); -// std::cout << "Result = " << (double)expVals[0] << "; expected " -// << theoryResults[count] << "\n"; -// EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); -// } -// } + int count = 0; + for (auto expVals : result.get_expectation_values().value()) { + EXPECT_EQ(expVals.size(), 1); + std::cout << "Result = " << (double)expVals[0] << "; expected " + << theoryResults[count] << "\n"; + EXPECT_NEAR((double)expVals[0], theoryResults[count++], 1e-3); + } +} From 0c9a219353a1dc7cae634e68b78618c01e759ff0 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 20 Feb 2025 02:52:19 +0000 Subject: [PATCH 298/311] [Code Cleanup] Create a global context to track the handle + scratch space Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/CMakeLists.txt | 1 + .../cudensitymat/CuDensityMatContext.cpp | 76 ++++++++++++++++ .../nvqir/cudensitymat/CuDensityMatContext.h | 35 ++++++++ .../nvqir/cudensitymat/CuDensityMatSim.cpp | 15 +--- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 7 +- .../nvqir/cudensitymat/cudm_expectation.cpp | 22 ++--- .../nvqir/cudensitymat/cudm_time_stepper.cpp | 19 ++-- .../dynamics/test_runge_kutta_integrator.cpp | 90 ------------------- 8 files changed, 127 insertions(+), 138 deletions(-) create mode 100644 runtime/nvqir/cudensitymat/CuDensityMatContext.cpp create mode 100644 runtime/nvqir/cudensitymat/CuDensityMatContext.h diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 04886e9e5e..51eba42425 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -33,6 +33,7 @@ add_library(${LIBRARY_NAME} SHARED cudm_expectation.cpp cudm_evolution.cpp CuDensityMatState.cpp + CuDensityMatContext.cpp ) message("CUDAToolkit_INCLUDE_DIRS = ${CUDAToolkit_INCLUDE_DIRS}") diff --git a/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp b/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp new file mode 100644 index 0000000000..5e5c14313f --- /dev/null +++ b/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "CuDensityMatContext.h" +#include "common/Logger.h" +#include "cudm_error_handling.h" +#include +#include + +namespace { +static std::unordered_map> + g_contexts; +static std::mutex g_contextMutex; +} // namespace + +namespace cudaq { +namespace dynamics { +Context *Context::getCurrentContext() { + int currentDevice = -1; + HANDLE_CUDA_ERROR(cudaGetDevice(¤tDevice)); + std::lock_guard guard(g_contextMutex); + const auto iter = g_contexts.find(currentDevice); + if (iter == g_contexts.end()) { + cudaq::info("Create cudensitymat context for device Id {}", currentDevice); + const auto [insertedIter, success] = g_contexts.emplace(std::make_pair( + currentDevice, std::unique_ptr(new Context(currentDevice)))); + if (!success) + throw std::runtime_error("Failed to create cudensitymat context"); + return insertedIter->second.get(); + } + + return iter->second.get(); +} + +void *Context::getScratchSpace(std::size_t minSizeBytes) { + if (minSizeBytes > m_scratchSpaceSizeBytes) { + // Realloc + if (m_scratchSpace) + HANDLE_CUDA_ERROR(cudaFree(m_scratchSpace)); + + cudaq::info("Allocate scratch buffer of size {} bytes on device {}", + minSizeBytes, m_deviceId); + + HANDLE_CUDA_ERROR(cudaMalloc(&m_scratchSpace, minSizeBytes)); + m_scratchSpaceSizeBytes = minSizeBytes; + } + + return m_scratchSpace; +} + +std::size_t Context::getRecommendedWorkSpaceLimit() { + std::size_t freeMem = 0, totalMem = 0; + HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); + // Take 80% of free memory + freeMem = static_cast(static_cast(freeMem) * 0.80); + return freeMem; +} + +Context::Context(int deviceId) : m_deviceId(deviceId) { + HANDLE_CUDA_ERROR(cudaSetDevice(deviceId)); + HANDLE_CUDM_ERROR(cudensitymatCreate(&m_cudmHandle)); +} + +Context::~Context() { + cudensitymatDestroy(m_cudmHandle); + if (m_scratchSpaceSizeBytes > 0) + cudaFree(m_scratchSpace); +} +} // namespace dynamics +// namespace dynamics +} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/CuDensityMatContext.h b/runtime/nvqir/cudensitymat/CuDensityMatContext.h new file mode 100644 index 0000000000..6876864d95 --- /dev/null +++ b/runtime/nvqir/cudensitymat/CuDensityMatContext.h @@ -0,0 +1,35 @@ +/*************************************************************** -*- C++ -*- *** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include +#include + +namespace cudaq { +namespace dynamics { +class Context { +public: + Context(Context const &) = delete; + Context &operator=(Context const &) = delete; + ~Context(); + + cudensitymatHandle_t getHandle() const { return m_cudmHandle; } + + static Context *getCurrentContext(); + void *getScratchSpace(std::size_t minSizeBytes); + static std::size_t getRecommendedWorkSpaceLimit(); + +private: + Context(int deviceId); + cudensitymatHandle_t m_cudmHandle; + int m_deviceId; + void *m_scratchSpace{nullptr}; + std::size_t m_scratchSpaceSizeBytes{0}; +}; +} // namespace dynamics +} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp b/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp index bae7ba125b..890c8c6d12 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatSim.cpp @@ -76,9 +76,6 @@ class CuDensityMatSim : public nvqir::CircuitSimulatorBase { using nvqir::CircuitSimulatorBase::shouldObserveFromSampling; using nvqir::CircuitSimulatorBase::summaryData; -private: - cudensitymatHandle_t m_cudmHandle = nullptr; - public: /// @brief The constructor CuDensityMatSim() { @@ -89,12 +86,10 @@ class CuDensityMatSim : public nvqir::CircuitSimulatorBase { if (cudaq::mpi::is_initialized()) initCuDensityMatCommLib(); HANDLE_CUDA_ERROR(cudaSetDevice(deviceId)); - HANDLE_CUDM_ERROR(cudensitymatCreate(&m_cudmHandle)); } /// The destructor - virtual ~CuDensityMatSim() { cudensitymatDestroy(m_cudmHandle); } - cudensitymatHandle_t getHandle() const { return m_cudmHandle; } + virtual ~CuDensityMatSim() {} std::unique_ptr getSimulationState() override { return std::make_unique(); } @@ -136,11 +131,3 @@ class CuDensityMatSim : public nvqir::CircuitSimulatorBase { } // namespace NVQIR_REGISTER_SIMULATOR(CuDensityMatSim, dynamics) - -extern "C" { -cudensitymatHandle_t getCudensitymatHandle() { - auto *sim = dynamic_cast(getCircuitSimulator_dynamics()); - assert(sim); - return sim->getHandle(); -} -} \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 555a6ad66f..aea415f7c2 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -6,6 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatContext.h" #include "CuDensityMatState.h" #include "cudaq/dynamics_integrators.h" #include "cudaq/evolution.h" @@ -13,8 +14,6 @@ #include "cudm_expectation.h" #include "cudm_helpers.h" #include "cudm_time_stepper.h" -#include -#include #include #include namespace cudaq { @@ -26,8 +25,8 @@ evolve_result evolve_single( &collapse_operators, const std::vector *> &observables, bool store_intermediate_results, std::optional shots_count) { - cudensitymatHandle_t handle; - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); + cudensitymatHandle_t handle = + dynamics::Context::getCurrentContext()->getHandle(); std::vector dims; for (const auto &[id, dim] : dimensions) dims.emplace_back(dim); diff --git a/runtime/nvqir/cudensitymat/cudm_expectation.cpp b/runtime/nvqir/cudensitymat/cudm_expectation.cpp index 46a120a75a..d43530e5aa 100644 --- a/runtime/nvqir/cudensitymat/cudm_expectation.cpp +++ b/runtime/nvqir/cudensitymat/cudm_expectation.cpp @@ -7,10 +7,10 @@ ******************************************************************************/ #include "cudm_expectation.h" +#include "CuDensityMatContext.h" #include "common/Logger.h" #include "cudm_error_handling.h" #include "cudm_helpers.h" -#include namespace cudaq { cudm_expectation::cudm_expectation(cudensitymatHandle_t handle, @@ -29,17 +29,12 @@ cudm_expectation::~cudm_expectation() { } void cudm_expectation::prepare(cudensitymatState_t state) { - std::size_t freeMem = 0, totalMem = 0; - HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); - freeMem = static_cast(static_cast(freeMem) * 0.80); - HANDLE_CUDM_ERROR(cudensitymatExpectationPrepare( - m_handle, m_expectation, state, CUDENSITYMAT_COMPUTE_64F, freeMem, - m_workspace, 0x0)); + m_handle, m_expectation, state, CUDENSITYMAT_COMPUTE_64F, + dynamics::Context::getRecommendedWorkSpaceLimit(), m_workspace, 0x0)); } std::complex cudm_expectation::compute(cudensitymatState_t state, double time) { - // TODO: create a global scratch buffer std::size_t requiredBufferSize = 0; HANDLE_CUDM_ERROR(cudensitymatWorkspaceGetMemorySize( m_handle, m_workspace, CUDENSITYMAT_MEMSPACE_DEVICE, @@ -49,11 +44,9 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, if (requiredBufferSize > 0) { cudaq::info("Required buffer size for expectation compute: {}", requiredBufferSize); - // Allocate GPU storage for workspace buffer - const std::size_t bufferVolume = - requiredBufferSize / sizeof(std::complex); - workspaceBuffer = cudm_helper::create_array_gpu( - std::vector>(bufferVolume, {0.0, 0.0})); + + workspaceBuffer = dynamics::Context::getCurrentContext()->getScratchSpace( + requiredBufferSize); // Attach workspace buffer HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( @@ -71,9 +64,6 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, sizeof(std::complex), cudaMemcpyDefault)); cudm_helper::destroy_array_gpu(expectationValue_d); - if (workspaceBuffer) { - cudm_helper::destroy_array_gpu(workspaceBuffer); - } return result; } } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp index 29938fb5f8..5fde9d979b 100644 --- a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp @@ -7,9 +7,9 @@ ******************************************************************************/ #include "cudm_time_stepper.h" +#include "CuDensityMatContext.h" #include "cudm_error_handling.h" #include "cudm_helpers.h" - namespace cudaq { cudmStepper::cudmStepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) @@ -32,12 +32,6 @@ state cudmStepper::compute( cudensitymatWorkspaceDescriptor_t workspace; HANDLE_CUDM_ERROR(cudensitymatCreateWorkspace(m_handle, &workspace)); - // Query free gpu memory and allocate workspace buffer - std::size_t freeMem = 0, totalMem = 0; - HANDLE_CUDA_ERROR(cudaMemGetInfo(&freeMem, &totalMem)); - // Take 80% of free memory - freeMem = static_cast(static_cast(freeMem) * 0.80); - // Create a new state for the next step auto next_state = CuDensityMatState::zero_like(state); @@ -54,7 +48,8 @@ state cudmStepper::compute( // Prepare the operator for action HANDLE_CUDM_ERROR(cudensitymatOperatorPrepareAction( m_handle, m_liouvillian, state.get_impl(), next_state.get_impl(), - CUDENSITYMAT_COMPUTE_64F, freeMem, workspace, 0x0)); + CUDENSITYMAT_COMPUTE_64F, + dynamics::Context::getRecommendedWorkSpaceLimit(), workspace, 0x0)); // Query required workspace buffer size std::size_t requiredBufferSize = 0; @@ -64,11 +59,8 @@ state cudmStepper::compute( void *workspaceBuffer = nullptr; if (requiredBufferSize > 0) { - // Allocate GPU storage for workspace buffer - const std::size_t bufferVolume = - requiredBufferSize / sizeof(std::complex); - workspaceBuffer = cudm_helper::create_array_gpu( - std::vector>(bufferVolume, {0.0, 0.0})); + workspaceBuffer = dynamics::Context::getCurrentContext()->getScratchSpace( + requiredBufferSize); // Attach workspace buffer HANDLE_CUDM_ERROR(cudensitymatWorkspaceSetMemory( @@ -91,7 +83,6 @@ state cudmStepper::compute( HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); // Cleanup - cudm_helper::destroy_array_gpu(workspaceBuffer); HANDLE_CUDM_ERROR(cudensitymatDestroyWorkspace(workspace)); return cudaq::state( diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index c7c7a2e307..4165000c9f 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -56,96 +56,6 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { ASSERT_NE(integrator_, nullptr); } -// Integration with Euler Method (substeps = 1) -TEST_F(RungeKuttaIntegratorTest, EulerIntegration) { - // auto integrator = std::make_unique( - // CuDensityMatState(handle_, mock_initial_state_data(), - // mock_hilbert_space_dims()), 0.0, time_stepper_, 1); - // integrator->set_option("dt", 0.1); - // EXPECT_NO_THROW(integrator->integrate(1.0)); -} - -// Integration with Midpoint Rule (substeps = 2) -TEST_F(RungeKuttaIntegratorTest, MidpointIntegration) { - // auto midpointIntegrator = std::make_unique( - // CuDensityMatState(handle_, mock_initial_state_data(), - // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); - // midpointIntegrator->set_option("dt", 0.1); - // EXPECT_NO_THROW(midpointIntegrator->integrate(1.0)); -} - -// Integration with Runge-Kutta 4 (substeps = 4, which is the default value) -TEST_F(RungeKuttaIntegratorTest, RungeKutta4Integration) { - // integrator_->set_option("dt", 0.1); - // EXPECT_NO_THROW(integrator_->integrate(1.0)); -} - -// Basic Integration Test -TEST_F(RungeKuttaIntegratorTest, BasicIntegration) { - // auto [t_before, state_before] = integrator_->get_state(); - // integrator_->set_option("dt", 0.1); - - // EXPECT_NO_THROW(integrator_->integrate(1.0)); - - // auto [t_after, state_after] = integrator_->get_state(); - // EXPECT_GT(t_after, t_before); -} - -// Multiple Integration Steps -TEST_F(RungeKuttaIntegratorTest, MultipleIntegrationSteps) { - // integrator_->set_option("dt", 0.1); - // integrator_->integrate(0.5); - // auto [t_mid, _] = integrator_->get_state(); - - // EXPECT_EQ(t_mid, 0.5); - - // integrator_->integrate(1.0); - // auto [t_final, __] = integrator_->get_state(); - - // EXPECT_EQ(t_final, 1.0); -} - -// Missing Time Step (dt) -TEST_F(RungeKuttaIntegratorTest, MissingTimeStepOption) { - // auto integrator_missing_dt = std::make_unique( - // CuDensityMatState(handle_, mock_initial_state_data(), - // mock_hilbert_space_dims()), 0.0, time_stepper_, 2); - - // EXPECT_THROW(integrator_missing_dt->integrate(1.0), std::invalid_argument); -} - -// Invalid Time Step (dt <= 0) -TEST_F(RungeKuttaIntegratorTest, InvalidTimeStepSize) { - // integrator_->set_option("dt", -0.1); - // EXPECT_THROW(integrator_->integrate(1.0), std::invalid_argument); -} - -// Zero Integration Time -TEST_F(RungeKuttaIntegratorTest, ZeroIntegrationTime) { - // auto [t_before, state_before] = integrator_->get_state(); - // integrator_->set_option("dt", 0.1); - - // EXPECT_NO_THROW(integrator_->integrate(0.0)); - - // auto [t_after, state_after] = integrator_->get_state(); - // EXPECT_EQ(t_before, t_after); -} - -// Large Time Step -TEST_F(RungeKuttaIntegratorTest, LargeTimeStep) { - // integrator_->set_option("dt", 100); - // EXPECT_NO_THROW(integrator_->integrate(0.0)); -} - -// Invalid Substeps -TEST_F(RungeKuttaIntegratorTest, InvalidSubsteps) { - // EXPECT_THROW(std::make_unique( - // CuDensityMatState(handle_, mock_initial_state_data(), - // mock_hilbert_space_dims()), - // 0.0, time_stepper_, 3), - // std::invalid_argument); -} - TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { cudm_helper helper(handle_); const std::vector> initialStateVec = {{1.0, 0.0}, From 9a4cc40ae8357017ebacc75a9951b8ba8ff6f00f Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 18 Feb 2025 11:08:04 +0000 Subject: [PATCH 299/311] getting updates from main Signed-off-by: Bettina Heim --- .github/workflows/publishing.yml | 2 +- docker/build/devdeps.ext.Dockerfile | 4 - docs/sphinx/using/backends/sims/tnsims.rst | 9 +- include/cudaq/Optimizer/Dialect/CC/CCOps.td | 41 +- include/cudaq/Optimizer/InitAllPasses.h | 1 + include/cudaq/Optimizer/Transforms/Passes.h | 1 + include/cudaq/Optimizer/Transforms/Passes.td | 29 +- lib/Optimizer/CodeGen/CCToLLVM.cpp | 35 +- lib/Optimizer/CodeGen/ConvertToQIRAPI.cpp | 100 +++- lib/Optimizer/Dialect/CC/CCOps.cpp | 44 ++ lib/Optimizer/Transforms/CMakeLists.txt | 2 + .../Transforms/ClassicalOptimization.cpp | 152 +++++ lib/Optimizer/Transforms/LiftArrayAlloc.cpp | 258 +------- .../Transforms/LiftArrayAllocPatterns.inc | 276 +++++++++ lib/Optimizer/Transforms/LoopAnalysis.cpp | 294 +++++++++- lib/Optimizer/Transforms/LoopAnalysis.h | 23 +- lib/Optimizer/Transforms/LoopNormalize.cpp | 138 +---- .../Transforms/LoopNormalizePatterns.inc | 169 ++++++ lib/Optimizer/Transforms/LoopUnroll.cpp | 244 +------- .../Transforms/LoopUnrollPatterns.inc | 217 +++++++ lib/Optimizer/Transforms/LowerToCFG.cpp | 76 +-- .../Transforms/LowerToCFGPatterns.inc | 104 ++++ .../Transforms/UpdateRegisterNames.cpp | 68 +++ .../Transforms/WriteAfterWriteElimination.cpp | 116 +--- .../WriteAfterWriteEliminationPatterns.inc | 129 ++++ python/cudaq/kernel/analysis.py | 2 +- python/cudaq/kernel/ast_bridge.py | 111 ++++ python/cudaq/kernels/uccsd.py | 146 +++-- .../cudaq/platform/py_alt_launch_kernel.cpp | 8 + python/tests/backends/test_IQM.py | 14 +- .../test_Ionq_LocalEmulation_kernel.py | 62 ++ python/tests/backends/test_braket.py | 21 +- python/tests/kernel/test_kernel_features.py | 58 +- python/tests/kernel/test_kernel_uccsd.py | 553 ++++++++++++++++++ runtime/common/BaseRemoteRESTQPU.h | 7 +- .../default/rest/helpers/anyon/anyon.yml | 2 +- .../rest/helpers/braket/BraketExecutor.cpp | 15 +- .../default/rest/helpers/braket/braket.yml | 2 +- .../rest/helpers/infleqtion/infleqtion.yml | 2 +- .../default/rest/helpers/ionq/ionq.yml | 2 +- .../platform/default/rest/helpers/iqm/iqm.yml | 2 +- .../platform/default/rest/helpers/oqc/oqc.yml | 2 +- .../rest/helpers/quantinuum/quantinuum.yml | 2 +- .../cudaq/platform/fermioniq/fermioniq.yml | 2 +- runtime/nvqir/cudensitymat/CMakeLists.txt | 1 + scripts/validate_container.sh | 24 + .../RegressionValidation/anyon.config | 2 +- .../RegressionValidation/ionq.config | 2 +- .../RegressionValidation/iqm.config | 2 +- .../RegressionValidation/oqc.config | 2 +- .../RegressionValidation/quantinuum.config | 2 +- targettests/execution/uccsd.cpp | 535 +++++++++++++++++ test/AST-Quake/infinite_loop.cpp | 63 ++ test/AST-Quake/loop_normal.cpp | 275 +++++++++ test/Quake/classical_optimization.qke | 253 ++++++++ test/Quake/qir_api_branching.qke | 96 +++ test/Quake/roundtrip-ops.qke | 17 + tools/nvqpp/nvq++.in | 5 +- unittests/CMakeLists.txt | 53 -- utils/mock_qpu/iqm/__init__.py | 2 +- 60 files changed, 3864 insertions(+), 1015 deletions(-) create mode 100644 lib/Optimizer/Transforms/ClassicalOptimization.cpp create mode 100644 lib/Optimizer/Transforms/LiftArrayAllocPatterns.inc create mode 100644 lib/Optimizer/Transforms/LoopNormalizePatterns.inc create mode 100644 lib/Optimizer/Transforms/LoopUnrollPatterns.inc create mode 100644 lib/Optimizer/Transforms/LowerToCFGPatterns.inc create mode 100644 lib/Optimizer/Transforms/UpdateRegisterNames.cpp create mode 100644 lib/Optimizer/Transforms/WriteAfterWriteEliminationPatterns.inc create mode 100644 python/tests/backends/test_Ionq_LocalEmulation_kernel.py create mode 100644 python/tests/kernel/test_kernel_uccsd.py create mode 100644 targettests/execution/uccsd.cpp create mode 100644 test/AST-Quake/infinite_loop.cpp create mode 100644 test/Quake/classical_optimization.qke create mode 100644 test/Quake/qir_api_branching.qke diff --git a/.github/workflows/publishing.yml b/.github/workflows/publishing.yml index 675a0f96da..9e7ca45624 100644 --- a/.github/workflows/publishing.yml +++ b/.github/workflows/publishing.yml @@ -839,7 +839,7 @@ jobs: fi; \ done` - rm -rf examples applications targets && mv github-repo/docs/sphinx/examples examples && mv github-repo/docs/sphinx/applications applications && mv github-repo/docs/sphinx/targets targets + rm -rf examples applications targets snippets && mv github-repo/docs/sphinx/examples examples && mv github-repo/docs/sphinx/applications applications && mv github-repo/docs/sphinx/targets targets && mv github-repo/docs/sphinx/snippets/python snippets mv github-repo/docs/notebook_validation.py . GITHUB_STEP_SUMMARY=$GITHUB_STEP_SUMMARY \ bash github-repo/scripts/validate_container.sh $backends_to_test | tee /tmp/validation.out diff --git a/docker/build/devdeps.ext.Dockerfile b/docker/build/devdeps.ext.Dockerfile index 343ffa3476..a04f82ddfa 100644 --- a/docker/build/devdeps.ext.Dockerfile +++ b/docker/build/devdeps.ext.Dockerfile @@ -181,7 +181,3 @@ ENV CUTENSOR_INSTALL_PREFIX="$CUTENSOR_INSTALL_PREFIX" ENV CUTENSOR_ROOT="$CUTENSOR_INSTALL_PREFIX" ENV LD_LIBRARY_PATH="$CUTENSOR_INSTALL_PREFIX/lib:$LD_LIBRARY_PATH" ENV CPATH="$CUTENSOR_INSTALL_PREFIX/include:$CPATH" - -# Active MPI support for the cuTensorNet library -RUN cd "$CUQUANTUM_INSTALL_PREFIX/distributed_interfaces/" && source activate_mpi_cutn.sh -ENV CUTENSORNET_COMM_LIB="$CUQUANTUM_INSTALL_PREFIX/distributed_interfaces/libcutensornet_distributed_interface_mpi.so" diff --git a/docs/sphinx/using/backends/sims/tnsims.rst b/docs/sphinx/using/backends/sims/tnsims.rst index 24c6fbd76a..2a2f56d11b 100644 --- a/docs/sphinx/using/backends/sims/tnsims.rst +++ b/docs/sphinx/using/backends/sims/tnsims.rst @@ -70,11 +70,12 @@ Use the following commands to enable distribution across multiple GPUs (adjust t mpiexec -np 2 ./program.x .. note:: + MPI parallelization on the :code:`tensornet` backend requires CUDA-Q's MPI support. + Please refer to the instructions on how to :ref:`enable MPI parallelization ` within CUDA-Q. + CUDA-Q containers are shipped with a pre-built MPI plugin; hence no additional setup is needed. - If the `CUTENSORNET_COMM_LIB` environment variable is not set, MPI parallelization on the :code:`tensornet` backend may fail. - If you are using a CUDA-Q container, this variable is pre-configured and no additional setup is needed. If you are customizing your installation or have built CUDA-Q from source, please follow the instructions for `activating the distributed interface `__ for the `cuTensorNet` library. This requires - :ref:`installing CUDA development dependencies `, and setting the `CUTENSORNET_COMM_LIB` - environment variable to the newly built `libcutensornet_distributed_interface_mpi.so` library. +.. note:: + If the `CUTENSORNET_COMM_LIB` environment variable is set following the activation procedure described in the `cuTensorNet documentation `__, the cuTensorNet MPI plugin will take precedence over the builtin support from CUDA-Q. Specific aspects of the simulation can be configured by setting the following of environment variables: diff --git a/include/cudaq/Optimizer/Dialect/CC/CCOps.td b/include/cudaq/Optimizer/Dialect/CC/CCOps.td index 8e059b41ed..9b1c3d7116 100644 --- a/include/cudaq/Optimizer/Dialect/CC/CCOps.td +++ b/include/cudaq/Optimizer/Dialect/CC/CCOps.td @@ -1459,8 +1459,8 @@ def cc_CallCallableOp : CCOp<"call_callable", [CallOpInterface]> { }]; } -def cc_CallIndirectCallableOp : CCOp<"call_indirect_callable", - [CallOpInterface]> { +def cc_CallIndirectCallableOp : + CCOp<"call_indirect_callable", [CallOpInterface]> { let summary = "Call a C++ callable, unresolved, at run-time."; let description = [{ This effectively connects a call from one kernel to another kernel, which @@ -1649,6 +1649,43 @@ def cc_CallableClosureOp : CCOp<"callable_closure", [Pure]> { }]; } +def cc_VarargCallOp : + CCOp<"call_vararg", [CallOpInterface, SymbolUserOpInterface]> { + let summary = "Create a call to an llvm.func with variadic arguments."; + let description = [{ + This operation lets us create a call to an LLVMIR FuncOp with variadic + arguments without the restriction that all the arguments have to be + converted to LLVMIR types first. These conversions are just code bloat and + make the code harder to read. + }]; + + let arguments = (ins + FlatSymbolRefAttr:$callee, + Variadic:$args + ); + let results = (outs Variadic); + + let assemblyFormat = [{ + $callee `(` $args `)` `:` functional-type(operands, results) attr-dict + }]; + + let extraClassDeclaration = [{ + operand_range getArgOperands() { + return {arg_operand_begin(), arg_operand_end()}; + } + + operand_iterator arg_operand_begin() { return operand_begin(); } + operand_iterator arg_operand_end() { return operand_end(); } + + /// Return the callee of this operation. + mlir::CallInterfaceCallable getCallableForCallee() { + return getCalleeAttr(); + } + + mlir::LogicalResult verifySymbolUses(mlir::SymbolTableCollection &); + }]; +} + def cc_CreateStringLiteralOp : CCOp<"string_literal"> { let summary = "Create a constant string literal."; let description = [{ diff --git a/include/cudaq/Optimizer/InitAllPasses.h b/include/cudaq/Optimizer/InitAllPasses.h index cda83274dd..91724d36a4 100644 --- a/include/cudaq/Optimizer/InitAllPasses.h +++ b/include/cudaq/Optimizer/InitAllPasses.h @@ -22,6 +22,7 @@ inline void registerCudaqPassesAndPipelines() { // CUDA-Q pipelines opt::registerAggressiveEarlyInliningPipeline(); opt::registerUnrollingPipeline(); + opt::registerClassicalOptimizationPipeline(); opt::registerToExecutionManagerCCPipeline(); opt::registerToQIRAPIPipeline(); opt::registerTargetPipelines(); diff --git a/include/cudaq/Optimizer/Transforms/Passes.h b/include/cudaq/Optimizer/Transforms/Passes.h index 4bfddf6101..bd9ef52b47 100644 --- a/include/cudaq/Optimizer/Transforms/Passes.h +++ b/include/cudaq/Optimizer/Transforms/Passes.h @@ -25,6 +25,7 @@ void addAggressiveEarlyInlining(mlir::OpPassManager &pm); void registerAggressiveEarlyInliningPipeline(); void registerUnrollingPipeline(); +void registerClassicalOptimizationPipeline(); void registerMappingPipeline(); std::unique_ptr createApplyOpSpecializationPass(); diff --git a/include/cudaq/Optimizer/Transforms/Passes.td b/include/cudaq/Optimizer/Transforms/Passes.td index 1ccec4a427..8b9b75b89e 100644 --- a/include/cudaq/Optimizer/Transforms/Passes.td +++ b/include/cudaq/Optimizer/Transforms/Passes.td @@ -141,9 +141,36 @@ def CheckKernelCalls : Pass<"check-kernel-calls", "mlir::func::FuncOp"> { }]; } +def ClassicalOptimization : Pass<"classical-optimization"> { + let summary = "Perform classical optimizations until a fix point is reached."; + let description = [{ + Performs a number of classical optimizations greedily until a fix point + is reached: + - canonicalization + - simplify regions + - write-after-write-elimination + - lift-array-alloc + - cc-loop-normalize + - cc-loop-unroll + }]; + + let dependentDialects = ["mlir::arith::ArithDialect", + "mlir::cf::ControlFlowDialect", + "cudaq::cc::CCDialect"]; + + let options = [ + Option<"threshold", "maximum-iterations", "unsigned", /*default=*/"50", + "Maximum iterations to unroll.">, + Option<"allowClosedInterval", "allow-closed-interval", "bool", + /*default=*/"true", "Allow loop iterations on a closed interval.">, + Option<"allowBreak", "allow-early-exit", "bool", /*default=*/"false", + "Allow unrolling of loop with early exit (i.e. break statement)."> + ]; +} + def CombineMeasurements : Pass<"combine-measurements", "mlir::func::FuncOp"> { - let summary = "Extends mesurements on subveqs adds output names"; + let summary = "Extends measurements on subveqs adds output names"; let description = [{ Replace a pattern such as: ``` diff --git a/lib/Optimizer/CodeGen/CCToLLVM.cpp b/lib/Optimizer/CodeGen/CCToLLVM.cpp index 5d8881ce44..4f6dd6fbf3 100644 --- a/lib/Optimizer/CodeGen/CCToLLVM.cpp +++ b/lib/Optimizer/CodeGen/CCToLLVM.cpp @@ -710,18 +710,33 @@ class UndefOpPattern : public ConvertOpToLLVMPattern { return success(); } }; + +class VarargCallPattern + : public ConvertOpToLLVMPattern { + using ConvertOpToLLVMPattern::ConvertOpToLLVMPattern; + + LogicalResult + matchAndRewrite(cudaq::cc::VarargCallOp vcall, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + SmallVector types; + for (auto ty : vcall.getResultTypes()) + types.push_back(getTypeConverter()->convertType(ty)); + rewriter.replaceOpWithNewOp(vcall, types, vcall.getCallee(), + adaptor.getArgs()); + return success(); + } +}; } // namespace void cudaq::opt::populateCCToLLVMPatterns(LLVMTypeConverter &typeConverter, RewritePatternSet &patterns) { - patterns.insert( - typeConverter); + patterns.insert< + AddressOfOpPattern, AllocaOpPattern, CallableClosureOpPattern, + CallableFuncOpPattern, CallCallableOpPattern, + CallIndirectCallableOpPattern, CastOpPattern, ComputePtrOpPattern, + CreateStringLiteralOpPattern, ExtractValueOpPattern, FuncToPtrOpPattern, + GlobalOpPattern, InsertValueOpPattern, InstantiateCallableOpPattern, + LoadOpPattern, OffsetOfOpPattern, PoisonOpPattern, SizeOfOpPattern, + StdvecDataOpPattern, StdvecInitOpPattern, StdvecSizeOpPattern, + StoreOpPattern, UndefOpPattern, VarargCallPattern>(typeConverter); } diff --git a/lib/Optimizer/CodeGen/ConvertToQIRAPI.cpp b/lib/Optimizer/CodeGen/ConvertToQIRAPI.cpp index fb48d7c753..6f8818855a 100644 --- a/lib/Optimizer/CodeGen/ConvertToQIRAPI.cpp +++ b/lib/Optimizer/CodeGen/ConvertToQIRAPI.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2022 - 2024 NVIDIA Corporation & Affiliates. * + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * * This source code and the accompanying materials are made available under * @@ -41,6 +41,9 @@ namespace cudaq::opt { using namespace mlir; +// Attribute name used to mark kernels that have been processed. +static constexpr const char FuncIsQIRAPI[] = "qir-api"; + //===----------------------------------------------------------------------===// static std::string getGateName(Operation *op) { @@ -1150,8 +1153,8 @@ struct QuantumGatePattern : public OpConversionPattern { args.append(opTargs.begin(), opTargs.end()); // Call the generalized version of the gate invocation. - rewriter.create(loc, TypeRange{}, - cudaq::opt::NVQIRGeneralizedInvokeAny, args); + rewriter.create( + loc, TypeRange{}, cudaq::opt::NVQIRGeneralizedInvokeAny, args); return forwardOrEraseOp(); } @@ -1228,17 +1231,30 @@ struct FuncSignaturePattern : public OpConversionPattern { auto funcTy = func.getFunctionType(); auto newFuncTy = cast(getTypeConverter()->convertType(funcTy)); - if (funcTy == newFuncTy) - return failure(); - if (funcTy.getNumInputs() && !func.getBody().empty()) { - // Replace the block argument types. - for (auto [blockArg, argTy] : llvm::zip( - func.getBody().front().getArguments(), newFuncTy.getInputs())) - blockArg.setType(argTy); + if (funcTy != newFuncTy) { + // Convert the entry block to the new argument types. + if (funcTy.getNumInputs() && !func.getBody().empty()) { + // Replace the block argument types. + for (auto [blockArg, argTy] : llvm::zip( + func.getBody().front().getArguments(), newFuncTy.getInputs())) + blockArg.setType(argTy); + } + } + // Convert any other blocks, as needed. + for (auto &block : func.getBody().getBlocks()) { + if (&block == &func.getBody().front()) + continue; + SmallVector newTypes; + for (auto blockArg : block.getArguments()) + newTypes.push_back(getTypeConverter()->convertType(blockArg.getType())); + for (auto [blockArg, newTy] : llvm::zip(block.getArguments(), newTypes)) + blockArg.setType(newTy); } // Replace the signature. - rewriter.updateRootInPlace(func, - [&]() { func.setFunctionType(newFuncTy); }); + rewriter.updateRootInPlace(func, [&]() { + func.setFunctionType(newFuncTy); + func->setAttr(FuncIsQIRAPI, rewriter.getUnitAttr()); + }); return success(); } }; @@ -1359,10 +1375,36 @@ struct CallOpInterfacePattern : public OpConversionPattern { using CallOpPattern = CallOpInterfacePattern; using CallIndirectOpPattern = CallOpInterfacePattern; +using CallVarargOpPattern = CallOpInterfacePattern; using CallCallableOpPattern = CallOpInterfacePattern; using CallIndirectCallableOpPattern = CallOpInterfacePattern; +struct BranchOpPattern : public OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(cf::BranchOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp(op, adaptor.getDestOperands(), + op.getDest()); + return success(); + } +}; + +struct CondBranchOpPattern : public OpConversionPattern { + using OpConversionPattern::OpConversionPattern; + + LogicalResult + matchAndRewrite(cf::CondBranchOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + rewriter.replaceOpWithNewOp( + op, adaptor.getCondition(), adaptor.getTrueDestOperands(), + adaptor.getFalseDestOperands(), op.getTrueDest(), op.getFalseDest()); + return success(); + } +}; + //===----------------------------------------------------------------------===// // Patterns that are common to all QIR conversions. //===----------------------------------------------------------------------===// @@ -1370,9 +1412,10 @@ using CallIndirectCallableOpPattern = static void commonClassicalHandlingPatterns(RewritePatternSet &patterns, TypeConverter &typeConverter, MLIRContext *ctx) { - patterns.insert(typeConverter, ctx); @@ -1590,8 +1633,10 @@ struct QuakeToQIRAPIPass target.addIllegalDialect(); target.addLegalOp(); - target.addDynamicallyLegalOp( - [&](func::FuncOp fn) { return !hasQuakeType(fn.getFunctionType()); }); + target.addDynamicallyLegalOp([&](func::FuncOp fn) { + return !hasQuakeType(fn.getFunctionType()) && + (!fn->hasAttr(cudaq::kernelAttrName) || fn->hasAttr(FuncIsQIRAPI)); + }); target.addDynamicallyLegalOp([&](func::ConstantOp fn) { return !hasQuakeType(fn.getResult().getType()); }); @@ -1615,19 +1660,26 @@ struct QuakeToQIRAPIPass [&](cudaq::cc::AllocaOp op) { return !hasQuakeType(op.getElementType()); }); - target.addDynamicallyLegalOp< - func::CallOp, func::CallIndirectOp, cudaq::cc::CallCallableOp, - cudaq::cc::CallIndirectCallableOp, cudaq::cc::CastOp, - cudaq::cc::FuncToPtrOp, cudaq::cc::StoreOp, cudaq::cc::LoadOp>( + target.addDynamicallyLegalOp( [&](Operation *op) { for (auto opnd : op->getOperands()) if (hasQuakeType(opnd.getType())) return false; - for (auto res : op->getResults()) - if (hasQuakeType(res.getType())) - return false; return true; }); + target.addDynamicallyLegalOp< + func::CallOp, func::CallIndirectOp, cudaq::cc::VarargCallOp, + cudaq::cc::CallCallableOp, cudaq::cc::CallIndirectCallableOp, + cudaq::cc::CastOp, cudaq::cc::FuncToPtrOp, cudaq::cc::StoreOp, + cudaq::cc::LoadOp>([&](Operation *op) { + for (auto opnd : op->getOperands()) + if (hasQuakeType(opnd.getType())) + return false; + for (auto res : op->getResults()) + if (hasQuakeType(res.getType())) + return false; + return true; + }); target.markUnknownOpDynamicallyLegal([](Operation *) { return true; }); if (failed(applyPartialConversion(op, target, std::move(patterns)))) signalPassFailure(); diff --git a/lib/Optimizer/Dialect/CC/CCOps.cpp b/lib/Optimizer/Dialect/CC/CCOps.cpp index 823c92de99..8a1cb39271 100644 --- a/lib/Optimizer/Dialect/CC/CCOps.cpp +++ b/lib/Optimizer/Dialect/CC/CCOps.cpp @@ -2347,6 +2347,50 @@ LogicalResult cudaq::cc::UnwindReturnOp::verify() { return success(); } +//===----------------------------------------------------------------------===// +// VarargCallOp +//===----------------------------------------------------------------------===// + +LogicalResult +cudaq::cc::VarargCallOp::verifySymbolUses(SymbolTableCollection &symbolTable) { + // Check that the callee attribute was specified. + auto fnAttr = (*this)->getAttrOfType("callee"); + if (!fnAttr) + return emitOpError("requires a 'callee' symbol reference attribute"); + LLVM::LLVMFuncOp fn = + symbolTable.lookupNearestSymbolFrom(*this, fnAttr); + if (!fn) + return emitOpError() << "'" << fnAttr.getValue() + << "' does not reference a valid LLVM function"; + + // Verify that the operand and result types match the callee. + auto fnType = fn.getFunctionType(); + if (fnType.getNumParams() > getNumOperands()) + return emitOpError("incorrect number of operands for callee"); + + for (unsigned i = 0, e = fnType.getNumParams(); i != e; ++i) + if (getOperand(i).getType() != fnType.getParams()[i]) { + return emitOpError("operand type mismatch: expected operand type ") + << fnType.getParams()[i] << ", but provided " + << getOperand(i).getType() << " for operand number " << i; + } + + if (fnType.getReturnType() == LLVM::LLVMVoidType::get(getContext()) && + getNumResults() == 0) + return success(); + + if (getNumResults() > 1) + return emitOpError("wrong number of result types: ") << getNumResults(); + + if (getResult(1).getType() != fnType.getReturnType()) { + auto diag = emitOpError("result type mismatch "); + diag.attachNote() << " op result types: " << getResultTypes(); + diag.attachNote() << "function result types: " << fnType.getReturnType(); + return diag; + } + return success(); +} + //===----------------------------------------------------------------------===// // Generated logic //===----------------------------------------------------------------------===// diff --git a/lib/Optimizer/Transforms/CMakeLists.txt b/lib/Optimizer/Transforms/CMakeLists.txt index d0a6b85799..55f20cc858 100644 --- a/lib/Optimizer/Transforms/CMakeLists.txt +++ b/lib/Optimizer/Transforms/CMakeLists.txt @@ -17,6 +17,7 @@ add_cudaq_library(OptTransforms ApplyOpSpecialization.cpp ArgumentSynthesis.cpp BasisConversion.cpp + ClassicalOptimization.cpp CombineMeasurements.cpp CombineQuantumAlloc.cpp ConstPropComplex.cpp @@ -54,6 +55,7 @@ add_cudaq_library(OptTransforms RegToMem.cpp StatePreparation.cpp UnitarySynthesis.cpp + UpdateRegisterNames.cpp WiresToWiresets.cpp WriteAfterWriteElimination.cpp diff --git a/lib/Optimizer/Transforms/ClassicalOptimization.cpp b/lib/Optimizer/Transforms/ClassicalOptimization.cpp new file mode 100644 index 0000000000..695efd37e0 --- /dev/null +++ b/lib/Optimizer/Transforms/ClassicalOptimization.cpp @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "LoopAnalysis.h" +#include "PassDetails.h" +#include "cudaq/Optimizer/Transforms/Passes.h" +#include "mlir/IR/Dominance.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "mlir/Transforms/Passes.h" +#include "mlir/Transforms/RegionUtils.h" + +namespace cudaq::opt { +#define GEN_PASS_DEF_CLASSICALOPTIMIZATION +#include "cudaq/Optimizer/Transforms/Passes.h.inc" +} // namespace cudaq::opt + +#define DEBUG_TYPE "classical-optimizations" + +using namespace mlir; + +#include "LiftArrayAllocPatterns.inc" +#include "LoopNormalizePatterns.inc" +#include "LoopUnrollPatterns.inc" +#include "LowerToCFGPatterns.inc" +#include "WriteAfterWriteEliminationPatterns.inc" + +namespace { + +/// The classical optimization pass performs a number of classical +/// optimizations greedily until changes no more changes can be done. +class ClassicalOptimizationPass + : public cudaq::opt::impl::ClassicalOptimizationBase< + ClassicalOptimizationPass> { +public: + using ClassicalOptimizationBase::ClassicalOptimizationBase; + + void runOnOperation() override { + auto *ctx = &getContext(); + auto *op = getOperation(); + DominanceInfo domInfo(op); + auto func = dyn_cast(op); + auto numLoops = countLoopOps(op); + unsigned progress = 0; + + RewritePatternSet patterns(ctx); + for (auto *dialect : ctx->getLoadedDialects()) + dialect->getCanonicalizationPatterns(patterns); + for (RegisteredOperationName op : ctx->getRegisteredOperations()) + op.getCanonicalizationPatterns(patterns, ctx); + + // Add patterns that help const prop loop boundaries computed + // in conditional statements, other loops, or arrays. + patterns.insert(ctx, /*rewriteOnlyIfConst=*/true); + patterns.insert(ctx, allowClosedInterval, allowBreak); + patterns.insert( + ctx, domInfo, func == nullptr ? "unknown" : func.getName()); + if (numLoops) + patterns.insert(ctx, threshold, + /*signalFailure=*/false, allowBreak, + progress); + + FrozenRewritePatternSet frozen(std::move(patterns)); + // Iterate over the loops until a fixed-point is reached. Some loops can + // only be unrolled if other loops are unrolled first and the constants + // iteratively propagated. + do { + // Remove overridden writes. + auto analysis = SimplifyWritesAnalysis(domInfo, op); + analysis.removeOverriddenStores(); + // Clean up dead code. + { + auto builder = OpBuilder(op); + IRRewriter rewriter(builder); + [[maybe_unused]] auto unused = + simplifyRegions(rewriter, op->getRegions()); + } + progress = 0; + (void)applyPatternsAndFoldGreedily(op, frozen); + } while (progress); + } + + static unsigned countLoopOps(Operation *op) { + unsigned result = 0; + op->walk([&](cudaq::cc::LoopOp loop) { result++; }); + LLVM_DEBUG(llvm::dbgs() << "Total number of loops: " << result << '\n'); + return result; + } +}; + +/// Classical optimization pipeline command-line options. These options are +/// similar to the ClassicalOptimization pass options, but have different +/// default settings. +struct ClassicalOptimizationPipelineOptions + : public PassPipelineOptions { + PassOptions::Option threshold{ + *this, "threshold", + llvm::cl::desc("Maximum iterations to unroll. (default: 1024)"), + llvm::cl::init(1024)}; + PassOptions::Option allowBreak{ + *this, "allow-early-exit", + llvm::cl::desc("Allow unrolling of loop with early exit (i.e. break " + "statement). (default: true)"), + llvm::cl::init(true)}; + PassOptions::Option allowClosedInterval{ + *this, "allow-closed-interval", + llvm::cl::desc("Allow unrolling of loop with a closed interval form. " + "(default: true)"), + llvm::cl::init(true)}; +}; +} // namespace + +/// Add a pass pipeline to apply the requisite passes to optimize classical +/// code. When converting to a quantum circuit, the static control program is +/// fully expanded to eliminate control flow. +static void createClassicalOptimizationPipeline(OpPassManager &pm, + unsigned threshold, + bool allowBreak, + bool allowClosedInterval) { + pm.addNestedPass(createCanonicalizerPass()); + pm.addNestedPass(createCSEPass()); + pm.addNestedPass(cudaq::opt::createClassicalMemToReg()); + + // Run classical optimization twice with a cse in between to optimize more + // code. + // TODO: run cse as a part of classical-optimization when we update the llvm + // version. + cudaq::opt::ClassicalOptimizationOptions options{ + threshold, allowClosedInterval, allowBreak}; + pm.addNestedPass( + cudaq::opt::createClassicalOptimization(options)); + pm.addNestedPass(createCSEPass()); + pm.addNestedPass( + cudaq::opt::createClassicalOptimization(options)); + pm.addNestedPass(cudaq::opt::createUpdateRegisterNames()); +} + +void cudaq::opt::registerClassicalOptimizationPipeline() { + PassPipelineRegistration( + "classical-optimization-pipeline", "Fully optimize classical code.", + [](OpPassManager &pm, + const ClassicalOptimizationPipelineOptions &options) { + createClassicalOptimizationPipeline(pm, options.threshold, + options.allowBreak, + options.allowClosedInterval); + }); +} diff --git a/lib/Optimizer/Transforms/LiftArrayAlloc.cpp b/lib/Optimizer/Transforms/LiftArrayAlloc.cpp index 0f7647b579..70113cf3e9 100644 --- a/lib/Optimizer/Transforms/LiftArrayAlloc.cpp +++ b/lib/Optimizer/Transforms/LiftArrayAlloc.cpp @@ -27,263 +27,9 @@ namespace cudaq::opt { using namespace mlir; -namespace { -class AllocaPattern : public OpRewritePattern { -public: - explicit AllocaPattern(MLIRContext *ctx, DominanceInfo &di, StringRef fn) - : OpRewritePattern(ctx), dom(di), funcName(fn) {} - - LogicalResult matchAndRewrite(cudaq::cc::AllocaOp alloc, - PatternRewriter &rewriter) const override { - SmallVector stores; - if (!isGoodCandidate(alloc, stores, dom)) - return failure(); - - LLVM_DEBUG(llvm::dbgs() << "Candidate was found\n"); - auto allocTy = alloc.getElementType(); - auto arrTy = cast(allocTy); - auto eleTy = arrTy.getElementType(); - - SmallVector values; - - // Every element of `stores` must be a cc::StoreOp with a ConstantOp as the - // value argument. Build the array attr to attach to a cc.const_array. - for (auto *op : stores) { - auto store = cast(op); - auto *valOp = store.getValue().getDefiningOp(); - if (auto con = dyn_cast(valOp)) - values.push_back(con.getValueAttr()); - else if (auto con = dyn_cast(valOp)) - values.push_back(con.getValueAttr()); - else - return alloc.emitOpError("could not fold"); - } - - // Create the cc.const_array. - auto valuesAttr = rewriter.getArrayAttr(values); - auto loc = alloc.getLoc(); - Value conArr = - rewriter.create(loc, arrTy, valuesAttr); - - assert(conArr && "must have created the constant array"); - LLVM_DEBUG(llvm::dbgs() << "constant array is:\n" << conArr << '\n'); - bool cannotEraseAlloc = false; - - // Collect all the stores, casts, and compute_ptr to be erased safely and in - // topological order. - SmallVector opsToErase; - auto insertOpToErase = [&](Operation *op) { - auto iter = std::find(opsToErase.begin(), opsToErase.end(), op); - if (iter == opsToErase.end()) - opsToErase.push_back(op); - }; - - // Rewalk all the uses of alloc, u, which must be cc.cast or cc.compute_ptr. - // For each u remove a store and replace a load with a cc.extract_value. - for (auto *user : alloc->getUsers()) { - if (!user) - continue; - std::int32_t offset = 0; - if (auto cptr = dyn_cast(user)) - offset = cptr.getRawConstantIndices()[0]; - bool isLive = false; - if (!isa(user)) { - cannotEraseAlloc = isLive = true; - } else { - for (auto *useuser : user->getUsers()) { - if (!useuser) - continue; - if (auto load = dyn_cast(useuser)) { - rewriter.setInsertionPointAfter(useuser); - LLVM_DEBUG(llvm::dbgs() << "replaced load\n"); - rewriter.replaceOpWithNewOp( - load, eleTy, conArr, - ArrayRef{offset}); - continue; - } - if (isa(useuser)) { - insertOpToErase(useuser); - continue; - } - LLVM_DEBUG(llvm::dbgs() << "alloc is live\n"); - cannotEraseAlloc = isLive = true; - } - } - if (!isLive) - insertOpToErase(user); - } - - for (auto *e : opsToErase) - rewriter.eraseOp(e); - - if (cannotEraseAlloc) { - rewriter.setInsertionPointAfter(alloc); - rewriter.create(loc, conArr, alloc); - return success(); - } - rewriter.eraseOp(alloc); - return success(); - } - - // Determine if \p alloc is a legit candidate for promotion to a constant - // array value. \p scoreboard is a vector of store operations. Each element of - // the allocated array must be written to exactly 1 time, and the scoreboard - // is used to track these stores. \p dom is the dominance info for this - // function (to ensure the stores happen before uses). - static bool isGoodCandidate(cudaq::cc::AllocaOp alloc, - SmallVectorImpl &scoreboard, - DominanceInfo &dom) { - LLVM_DEBUG(llvm::dbgs() << "checking candidate\n"); - if (alloc.getSeqSize()) - return false; - auto arrTy = dyn_cast(alloc.getElementType()); - if (!arrTy || arrTy.isUnknownSize()) - return false; - auto arrEleTy = arrTy.getElementType(); - if (!isa(arrEleTy)) - return false; - - // There must be at least `size` uses to initialize the entire array. - auto size = arrTy.getSize(); - if (std::distance(alloc->getUses().begin(), alloc->getUses().end()) < size) - return false; - - // Keep a scoreboard for every element in the array. Every element *must* be - // stored to with a constant exactly one time. - scoreboard.resize(size); - for (int i = 0; i < size; i++) - scoreboard[i] = nullptr; - - SmallVector toGlobalUses; - SmallVector> loadSets(size); - - auto getWriteOp = [&](auto op, std::int32_t index) -> Operation * { - Operation *theStore = nullptr; - for (auto &use : op->getUses()) { - Operation *u = use.getOwner(); - if (!u) - return nullptr; - if (auto store = dyn_cast(u)) { - if (op.getOperation() == store.getPtrvalue().getDefiningOp()) { - if (theStore) { - LLVM_DEBUG(llvm::dbgs() - << "more than 1 store to element of array\n"); - return nullptr; - } - LLVM_DEBUG(llvm::dbgs() << "found store: " << store << "\n"); - theStore = u; - } - continue; - } - if (isa(u)) { - toGlobalUses.push_back(u); - continue; - } - if (isa(u)) { - loadSets[index].insert(u); - continue; - } - return nullptr; - } - return theStore && - isa_and_present( - dyn_cast(theStore) - .getValue() - .getDefiningOp()) - ? theStore - : nullptr; - }; - - auto unsizedArrTy = cudaq::cc::ArrayType::get(arrEleTy); - auto ptrUnsizedArrTy = cudaq::cc::PointerType::get(unsizedArrTy); - auto ptrArrEleTy = cudaq::cc::PointerType::get(arrEleTy); - for (auto &use : alloc->getUses()) { - // All uses *must* be a degenerate cc.cast, cc.compute_ptr, or - // cc.init_state. - auto *op = use.getOwner(); - if (!op) { - LLVM_DEBUG(llvm::dbgs() << "use was not an op\n"); - return false; - } - if (auto cptr = dyn_cast(op)) { - if (auto index = cptr.getConstantIndex(0)) - if (auto w = getWriteOp(cptr, *index)) - if (!scoreboard[*index]) { - scoreboard[*index] = w; - continue; - } - return false; - } - if (auto cast = dyn_cast(op)) { - // Process casts that are used in store ops. - if (cast.getType() == ptrArrEleTy) { - if (auto w = getWriteOp(cast, 0)) - if (!scoreboard[0]) { - scoreboard[0] = w; - continue; - } - return false; - } - // Process casts that are used in quake.init_state. - if (cast.getType() == ptrUnsizedArrTy) { - if (cast->hasOneUse()) { - auto &use = *cast->getUses().begin(); - Operation *u = use.getOwner(); - if (isa_and_present(u)) { - toGlobalUses.push_back(op); - continue; - } - } - return false; - } - LLVM_DEBUG(llvm::dbgs() << "unexpected cast: " << *op << '\n'); - toGlobalUses.push_back(op); - continue; - } - if (isa(op)) { - toGlobalUses.push_back(op); - continue; - } - LLVM_DEBUG(llvm::dbgs() << "unexpected use: " << *op << '\n'); - toGlobalUses.push_back(op); - } - - bool ok = std::all_of(scoreboard.begin(), scoreboard.end(), - [](bool b) { return b; }); - LLVM_DEBUG(llvm::dbgs() << "all elements of array are set: " << ok << '\n'); - if (ok) { - // Verify dominance relations. - - // For all stores, the store of an element $e$ must dominate all loads of - // $e$. - for (int i = 0; i < size; ++i) { - for (auto *load : loadSets[i]) - if (!dom.dominates(scoreboard[i], load)) { - LLVM_DEBUG(llvm::dbgs() - << "store " << scoreboard[i] - << " doesn't dominate load: " << *load << '\n'); - return false; - } - } - - // For all global uses, all of the stores must dominate every use. - for (auto *glob : toGlobalUses) { - for (auto *store : scoreboard) - if (!dom.dominates(store, glob)) { - LLVM_DEBUG(llvm::dbgs() - << "store " << store << " doesn't dominate op: " << *glob - << '\n'); - return false; - } - } - } - return ok; - } - - DominanceInfo &dom; - StringRef funcName; -}; +#include "LiftArrayAllocPatterns.inc" +namespace { class LiftArrayAllocPass : public cudaq::opt::impl::LiftArrayAllocBase { public: diff --git a/lib/Optimizer/Transforms/LiftArrayAllocPatterns.inc b/lib/Optimizer/Transforms/LiftArrayAllocPatterns.inc new file mode 100644 index 0000000000..d87995ffb8 --- /dev/null +++ b/lib/Optimizer/Transforms/LiftArrayAllocPatterns.inc @@ -0,0 +1,276 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// These patterns are used by the loft-array-alloc and cc-loop-unroll passes. + +// This file must be included after a `using namespace mlir;` as it uses bare +// identifiers from that namespace. + +namespace { +class AllocaPattern : public OpRewritePattern { +public: + explicit AllocaPattern(MLIRContext *ctx, DominanceInfo &di, StringRef fn) + : OpRewritePattern(ctx), dom(di), funcName(fn) {} + + LogicalResult matchAndRewrite(cudaq::cc::AllocaOp alloc, + PatternRewriter &rewriter) const override { + SmallVector stores; + if (!isGoodCandidate(alloc, stores, dom)) + return failure(); + + LLVM_DEBUG(llvm::dbgs() << "Candidate was found\n"); + auto allocTy = alloc.getElementType(); + auto arrTy = cast(allocTy); + auto eleTy = arrTy.getElementType(); + + SmallVector values; + + // Every element of `stores` must be a cc::StoreOp with a ConstantOp as the + // value argument. Build the array attr to attach to a cc.const_array. + for (auto *op : stores) { + auto store = cast(op); + auto *valOp = store.getValue().getDefiningOp(); + if (auto con = dyn_cast(valOp)) + values.push_back(con.getValueAttr()); + else if (auto con = dyn_cast(valOp)) + values.push_back(con.getValueAttr()); + else + return alloc.emitOpError("could not fold"); + } + + // Create the cc.const_array. + auto valuesAttr = rewriter.getArrayAttr(values); + auto loc = alloc.getLoc(); + Value conArr = + rewriter.create(loc, arrTy, valuesAttr); + + assert(conArr && "must have created the constant array"); + LLVM_DEBUG(llvm::dbgs() << "constant array is:\n" << conArr << '\n'); + bool cannotEraseAlloc = false; + + // Collect all the stores, casts, and compute_ptr to be erased safely and in + // topological order. + SmallVector opsToErase; + auto insertOpToErase = [&](Operation *op) { + auto iter = std::find(opsToErase.begin(), opsToErase.end(), op); + if (iter == opsToErase.end()) + opsToErase.push_back(op); + }; + + // Rewalk all the uses of alloc, u, which must be cc.cast or cc.compute_ptr. + // For each u remove a store and replace a load with a cc.extract_value. + for (auto *user : alloc->getUsers()) { + if (!user) + continue; + std::int32_t offset = 0; + if (auto cptr = dyn_cast(user)) + offset = cptr.getRawConstantIndices()[0]; + bool isLive = false; + if (!isa(user)) { + cannotEraseAlloc = isLive = true; + } else { + for (auto *useuser : user->getUsers()) { + if (!useuser) + continue; + if (auto load = dyn_cast(useuser)) { + rewriter.setInsertionPointAfter(useuser); + LLVM_DEBUG(llvm::dbgs() << "replaced load\n"); + // rewriter.replaceOpWithNewOp( + // load, eleTy, conArr, + // ArrayRef{offset}); + + auto extractValue = rewriter.create( + loc, eleTy, conArr, + ArrayRef{offset}); + rewriter.replaceAllUsesWith(load, extractValue); + insertOpToErase(load); + continue; + } + if (isa(useuser)) { + insertOpToErase(useuser); + continue; + } + LLVM_DEBUG(llvm::dbgs() << "alloc is live\n"); + cannotEraseAlloc = isLive = true; + } + } + if (!isLive) + insertOpToErase(user); + } + + for (auto *e : opsToErase) + rewriter.eraseOp(e); + + if (cannotEraseAlloc) { + rewriter.setInsertionPointAfter(alloc); + rewriter.create(loc, conArr, alloc); + return success(); + } + rewriter.eraseOp(alloc); + return success(); + } + + // Determine if \p alloc is a legit candidate for promotion to a constant + // array value. \p scoreboard is a vector of store operations. Each element of + // the allocated array must be written to exactly 1 time, and the scoreboard + // is used to track these stores. \p dom is the dominance info for this + // function (to ensure the stores happen before uses). + static bool isGoodCandidate(cudaq::cc::AllocaOp alloc, + SmallVectorImpl &scoreboard, + DominanceInfo &dom) { + if (alloc.getSeqSize()) + return false; + auto arrTy = dyn_cast(alloc.getElementType()); + if (!arrTy || arrTy.isUnknownSize()) + return false; + auto arrEleTy = arrTy.getElementType(); + if (!isa(arrEleTy)) + return false; + + // There must be at least `size` uses to initialize the entire array. + auto size = arrTy.getSize(); + if (std::distance(alloc->getUses().begin(), alloc->getUses().end()) < size) + return false; + + // Keep a scoreboard for every element in the array. Every element *must* be + // stored to with a constant exactly one time. + scoreboard.resize(size); + for (int i = 0; i < size; i++) + scoreboard[i] = nullptr; + + SmallVector toGlobalUses; + SmallVector> loadSets(size); + + auto getWriteOp = [&](auto op, std::int32_t index) -> Operation * { + Operation *theStore = nullptr; + for (auto &use : op->getUses()) { + Operation *u = use.getOwner(); + if (!u) + return nullptr; + + if (auto store = dyn_cast(u)) { + if (op.getOperation() == store.getPtrvalue().getDefiningOp()) { + if (theStore) { + LLVM_DEBUG(llvm::dbgs() + << "more than 1 store to element of array\n"); + return nullptr; + } + LLVM_DEBUG(llvm::dbgs() << "found store: " << store << "\n"); + theStore = u; + } + continue; + } + if (isa(u)) { + toGlobalUses.push_back(u); + continue; + } + if (isa(u)) { + loadSets[index].insert(u); + continue; + } + return nullptr; + } + return theStore && + isa_and_present( + dyn_cast(theStore) + .getValue() + .getDefiningOp()) + ? theStore + : nullptr; + }; + + auto unsizedArrTy = cudaq::cc::ArrayType::get(arrEleTy); + auto ptrUnsizedArrTy = cudaq::cc::PointerType::get(unsizedArrTy); + auto ptrArrEleTy = cudaq::cc::PointerType::get(arrEleTy); + for (auto &use : alloc->getUses()) { + // All uses *must* be a degenerate cc.cast, cc.compute_ptr, or + // cc.init_state. + auto *op = use.getOwner(); + if (!op) { + LLVM_DEBUG(llvm::dbgs() << "use was not an op\n"); + return false; + } + if (auto cptr = dyn_cast(op)) { + if (auto index = cptr.getConstantIndex(0)) + if (auto w = getWriteOp(cptr, *index)) + if (!scoreboard[*index]) { + scoreboard[*index] = w; + continue; + } + return false; + } + if (auto cast = dyn_cast(op)) { + // Process casts that are used in store ops. + if (cast.getType() == ptrArrEleTy) { + if (auto w = getWriteOp(cast, 0)) + if (!scoreboard[0]) { + scoreboard[0] = w; + continue; + } + return false; + } + // Process casts that are used in quake.init_state. + if (cast.getType() == ptrUnsizedArrTy) { + if (cast->hasOneUse()) { + auto &use = *cast->getUses().begin(); + Operation *u = use.getOwner(); + if (isa_and_present(u)) { + toGlobalUses.push_back(op); + continue; + } + } + return false; + } + LLVM_DEBUG(llvm::dbgs() << "unexpected cast: " << *op << '\n'); + toGlobalUses.push_back(op); + continue; + } + if (isa(op)) { + toGlobalUses.push_back(op); + continue; + } + LLVM_DEBUG(llvm::dbgs() << "unexpected use: " << *op << '\n'); + toGlobalUses.push_back(op); + } + + bool ok = std::all_of(scoreboard.begin(), scoreboard.end(), + [](bool b) { return b; }); + LLVM_DEBUG(llvm::dbgs() << "all elements of array are set: " << ok << '\n'); + if (ok) { + // Verify dominance relations. + + // For all stores, the store of an element $e$ must dominate all loads of + // $e$. + for (int i = 0; i < size; ++i) { + for (auto *load : loadSets[i]) + if (!dom.dominates(scoreboard[i], load)) { + LLVM_DEBUG(llvm::dbgs() + << "store " << scoreboard[i] + << " doesn't dominate load: " << *load << '\n'); + return false; + } + } + + // For all global uses, all of the stores must dominate every use. + for (auto *glob : toGlobalUses) { + for (auto *store : scoreboard) + if (!dom.dominates(store, glob)) { + LLVM_DEBUG(llvm::dbgs() + << "store " << store << " doesn't dominate op: " << *glob + << '\n'); + return false; + } + } + } + return ok; + } + + DominanceInfo &dom; + StringRef funcName; +}; +} // namespace diff --git a/lib/Optimizer/Transforms/LoopAnalysis.cpp b/lib/Optimizer/Transforms/LoopAnalysis.cpp index c40e2b7e30..d069c6eca0 100644 --- a/lib/Optimizer/Transforms/LoopAnalysis.cpp +++ b/lib/Optimizer/Transforms/LoopAnalysis.cpp @@ -7,6 +7,7 @@ ******************************************************************************/ #include "LoopAnalysis.h" +#include "cudaq/Optimizer/Builder/Factory.h" #include "mlir/IR/Dominance.h" using namespace mlir; @@ -210,6 +211,10 @@ static BlockArgument getLinearExpr(Value expr, return scaledIteration(expr); } +static unsigned bitWidth(Value val) { + return cast(val.getType()).getWidth(); +} + namespace cudaq { bool opt::isSemiOpenPredicate(arith::CmpIPredicate p) { @@ -223,6 +228,11 @@ bool opt::isUnsignedPredicate(arith::CmpIPredicate p) { p == arith::CmpIPredicate::ugt || p == arith::CmpIPredicate::uge; } +bool opt::isSignedPredicate(arith::CmpIPredicate p) { + return p == arith::CmpIPredicate::slt || p == arith::CmpIPredicate::sle || + p == arith::CmpIPredicate::sgt || p == arith::CmpIPredicate::sge; +} + // We expect the loop control value to have the following form. // // %final = cc.loop while ((%iter = %initial) -> (iN)) { @@ -314,26 +324,296 @@ bool opt::isaConstantUpperBoundLoop(cc::LoopOp loop, bool allowClosedInterval) { isaConstant(c.compareValue); } -Value opt::LoopComponents::getCompareInduction() { +Value opt::LoopComponents::getCompareInduction() const { auto cmpOp = cast(compareOp); return cmpOp.getLhs() == compareValue ? cmpOp.getRhs() : cmpOp.getLhs(); } -bool opt::LoopComponents::stepIsAnAddOp() { return isa(stepOp); } +bool opt::LoopComponents::stepIsAnAddOp() const { + return isa(stepOp); +} -bool opt::LoopComponents::shouldCommuteStepOp() { +bool opt::LoopComponents::shouldCommuteStepOp() const { if (auto addOp = dyn_cast_or_null(stepOp)) return addOp.getRhs() == stepRegion->front().getArgument(induction); // Note: we don't allow induction on lhs of subtraction. return false; } -bool opt::LoopComponents::isClosedIntervalForm() { +bool opt::LoopComponents::isClosedIntervalForm() const { auto cmp = cast(compareOp); return ::isClosedIntervalForm(cmp.getPredicate()); } -bool opt::LoopComponents::isLinearExpr() { return addendValue || scaleValue; } +bool opt::LoopComponents::isLinearExpr() const { + return addendValue || scaleValue; +} + +std::int64_t opt::LoopComponents::extendValue(unsigned width, + std::size_t val) const { + const bool signExt = + isSignedPredicate(cast(compareOp).getPredicate()); + std::int64_t result = val; + switch (width) { + case 8: + if (signExt) { + std::int8_t v = val & 0xFF; + result = v; + } else { + std::uint8_t v = val & 0xFF; + result = v; + } + break; + case 16: + if (signExt) { + std::int16_t v = val & 0xFFFF; + result = v; + } else { + std::uint16_t v = val & 0xFFFF; + result = v; + } + break; + case 32: + if (signExt) { + std::int32_t v = val & 0xFFFFFFFF; + result = v; + } else { + std::uint32_t v = val & 0xFFFFFFFF; + result = v; + } + break; + default: + break; + } + return result; +} + +bool opt::LoopComponents::hasAlwaysTrueCondition() const { + auto cmpValOpt = factory::maybeValueOfIntConstant(compareValue); + if (!cmpValOpt) + return false; + auto width = bitWidth(compareValue); + std::int64_t cmpVal = *cmpValOpt; + auto pred = cast(compareOp).getPredicate(); + switch (width) { + case 8: { + switch (pred) { + case arith::CmpIPredicate::sge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sle: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::uge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ule: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + case 16: { + switch (pred) { + case arith::CmpIPredicate::sge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sle: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::uge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ule: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + case 32: { + switch (pred) { + case arith::CmpIPredicate::sge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sle: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::uge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ule: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + case 64: { + switch (pred) { + case arith::CmpIPredicate::sge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sle: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::uge: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ule: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + default: + break; + } + return false; +} + +bool opt::LoopComponents::hasAlwaysFalseCondition() const { + auto cmpValOpt = factory::maybeValueOfIntConstant(compareValue); + if (!cmpValOpt) + return false; + auto width = bitWidth(compareValue); + std::int64_t cmpVal = *cmpValOpt; + auto pred = cast(compareOp).getPredicate(); + switch (width) { + case 8: { + switch (pred) { + case arith::CmpIPredicate::slt: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sgt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::ult: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ugt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + case 16: { + switch (pred) { + case arith::CmpIPredicate::slt: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sgt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::ult: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ugt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + case 32: { + switch (pred) { + case arith::CmpIPredicate::slt: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sgt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::ult: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ugt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + case 64: { + switch (pred) { + case arith::CmpIPredicate::slt: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::sgt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + case arith::CmpIPredicate::ult: + return static_cast(cmpVal) == + std::numeric_limits::min(); + case arith::CmpIPredicate::ugt: + return static_cast(cmpVal) == + std::numeric_limits::max(); + default: + break; + } + } break; + default: + break; + } + return false; +} + +std::optional opt::LoopComponents::getIterationsConstant() const { + auto initValOpt = factory::maybeValueOfIntConstant(initialValue); + if (!initValOpt) + return std::nullopt; + std::int64_t initVal = extendValue(bitWidth(initialValue), *initValOpt); + auto endValOpt = factory::maybeValueOfIntConstant(compareValue); + if (!endValOpt) + return std::nullopt; + std::int64_t endVal = extendValue(bitWidth(compareValue), *endValOpt); + auto stepValOpt = factory::maybeValueOfIntConstant(stepValue); + if (!stepValOpt) + return std::nullopt; + std::int64_t stepVal = extendValue(bitWidth(stepValue), *stepValOpt); + if (!stepIsAnAddOp()) + stepVal = -stepVal; + if (isLinearExpr()) { + if (addendValue) { + auto addendOpt = factory::maybeValueOfIntConstant(addendValue); + if (!addendOpt) + return std::nullopt; + std::int64_t addend = extendValue(bitWidth(addendValue), *addendOpt); + if (negatedAddend) + endVal += addend; + else + endVal -= addend; + } + if (minusOneMult) { + initVal = -initVal; + stepVal = -stepVal; + } + if (scaleValue) { + auto scaleValOpt = factory::maybeValueOfIntConstant(scaleValue); + if (!scaleValOpt) + return std::nullopt; + std::int64_t scaleVal = extendValue(bitWidth(scaleValue), *scaleValOpt); + if (reciprocalScale) { + endVal *= scaleVal; + } else { + endVal *= scaleVal; + stepVal *= scaleVal; + } + } + } + if (!isClosedIntervalForm()) { + if (stepVal < 0) + endVal += 1; + else + endVal -= 1; + } + std::int64_t result = (endVal - initVal + stepVal) / stepVal; + if (result < 0) + result = 0; + return {result}; +} template constexpr int computeArgsOffset() { @@ -350,7 +630,9 @@ std::optional opt::getLoopComponents(cc::LoopOp loop) { auto &whileEntry = whileRegion.front(); auto condOp = cast(whileRegion.back().back()); result.compareOp = condOp.getCondition().getDefiningOp(); - auto cmpOp = cast(result.compareOp); + auto cmpOp = dyn_cast(result.compareOp); + if (!cmpOp) + return {}; auto argumentToCompare = [&](unsigned idx) -> bool { return (getLinearExpr(cmpOp.getLhs(), result, loop) == diff --git a/lib/Optimizer/Transforms/LoopAnalysis.h b/lib/Optimizer/Transforms/LoopAnalysis.h index 1d2f6181f0..12f7310655 100644 --- a/lib/Optimizer/Transforms/LoopAnalysis.h +++ b/lib/Optimizer/Transforms/LoopAnalysis.h @@ -14,17 +14,29 @@ namespace cudaq::opt { // Loops that are transformed into normal form have this attribute. static constexpr char NormalizedLoopAttr[] = "normalized"; +static constexpr char DeadLoopAttr[] = "dead"; struct LoopComponents { LoopComponents() = default; // Get the induction expression of the comparison. - mlir::Value getCompareInduction(); + mlir::Value getCompareInduction() const; - bool stepIsAnAddOp(); - bool shouldCommuteStepOp(); - bool isClosedIntervalForm(); - bool isLinearExpr(); + bool stepIsAnAddOp() const; + bool shouldCommuteStepOp() const; + bool isClosedIntervalForm() const; + bool isLinearExpr() const; + std::optional getIterationsConstant() const; + + // Determine if the condition is always true. e.g., `x uge 0`. + bool hasAlwaysTrueCondition() const; + // Determine if the condition is always false. e.g., `x ult 0`. + bool hasAlwaysFalseCondition() const; + bool hasInvariantCondition() const { + return hasAlwaysTrueCondition() || hasAlwaysFalseCondition(); + } + + std::int64_t extendValue(unsigned width, std::size_t val) const; unsigned induction = 0; mlir::Value initialValue; @@ -50,6 +62,7 @@ struct LoopComponents { /// Does boundary test defines a semi-open interval? bool isSemiOpenPredicate(mlir::arith::CmpIPredicate p); bool isUnsignedPredicate(mlir::arith::CmpIPredicate p); +bool isSignedPredicate(mlir::arith::CmpIPredicate p); /// A counted loop is defined to be a loop that will execute some compile-time /// constant number of iterations. We recognize a normalized, semi-open interval diff --git a/lib/Optimizer/Transforms/LoopNormalize.cpp b/lib/Optimizer/Transforms/LoopNormalize.cpp index faf0b3ea64..2e15bbb069 100644 --- a/lib/Optimizer/Transforms/LoopNormalize.cpp +++ b/lib/Optimizer/Transforms/LoopNormalize.cpp @@ -23,145 +23,9 @@ namespace cudaq::opt { using namespace mlir; -// Return true if \p loop is not monotonic or it is an invariant loop. -// Normalization is to be done on any loop that is monotonic and not -// invariant (which includes loops that are already in counted form). -static bool isNotMonotonicOrInvariant(cudaq::cc::LoopOp loop, - bool allowClosedInterval, - bool allowEarlyExit) { - cudaq::opt::LoopComponents c; - return !cudaq::opt::isaMonotonicLoop(loop, allowEarlyExit, &c) || - (cudaq::opt::isaInvariantLoop(c, allowClosedInterval) && - !c.isLinearExpr()); -} +#include "LoopNormalizePatterns.inc" namespace { -class LoopPat : public OpRewritePattern { -public: - explicit LoopPat(MLIRContext *ctx, bool aci, bool ab) - : OpRewritePattern(ctx), allowClosedInterval(aci), allowEarlyExit(ab) {} - - LogicalResult matchAndRewrite(cudaq::cc::LoopOp loop, - PatternRewriter &rewriter) const override { - if (loop->hasAttr(cudaq::opt::NormalizedLoopAttr)) - return failure(); - if (isNotMonotonicOrInvariant(loop, allowClosedInterval, allowEarlyExit)) - return failure(); - - // loop is monotonic but not invariant. - LLVM_DEBUG(llvm::dbgs() << "loop before normalization: " << loop << '\n'); - auto componentsOpt = cudaq::opt::getLoopComponents(loop); - assert(componentsOpt && "loop must have components"); - auto c = *componentsOpt; - auto loc = loop.getLoc(); - - // 1) Set initial value to 0. - auto ty = c.initialValue.getType(); - rewriter.startRootUpdate(loop); - auto createConstantOp = [&](std::int64_t val) -> Value { - return rewriter.create(loc, val, ty); - }; - auto zero = createConstantOp(0); - loop->setOperand(c.induction, zero); - - // 2) Compute the number of iterations as an invariant. `iterations = max(0, - // (upper - lower + step) / step)`. - Value upper = c.compareValue; - auto one = createConstantOp(1); - Value step = c.stepValue; - Value lower = c.initialValue; - if (!c.stepIsAnAddOp()) - step = rewriter.create(loc, zero, step); - if (c.isLinearExpr()) { - // Induction is part of a linear expression. Deal with the terms of the - // equation. `m` scales the step. `b` is an addend to the lower bound. - if (c.addendValue) { - if (c.negatedAddend) { - // `m * i - b`, u += `b`. - upper = rewriter.create(loc, upper, c.addendValue); - } else { - // `m * i + b`, u -= `b`. - upper = rewriter.create(loc, upper, c.addendValue); - } - } - if (c.minusOneMult) { - // `b - m * i` (b eliminated), multiply lower and step by `-1` (`m` - // follows). - auto negOne = createConstantOp(-1); - lower = rewriter.create(loc, lower, negOne); - step = rewriter.create(loc, step, negOne); - } - if (c.scaleValue) { - if (c.reciprocalScale) { - // `1/m * i + b` (b eliminated), multiply upper by `m`. - upper = rewriter.create(loc, upper, c.scaleValue); - } else { - // `m * i + b` (b eliminated), multiple lower and step by `m`. - lower = rewriter.create(loc, lower, c.scaleValue); - step = rewriter.create(loc, step, c.scaleValue); - } - } - } - if (!c.isClosedIntervalForm()) { - // Note: treating the step as a signed value to process countdown loops as - // well as countup loops. - Value negStepCond = rewriter.create( - loc, arith::CmpIPredicate::slt, step, zero); - auto negOne = createConstantOp(-1); - Value adj = - rewriter.create(loc, ty, negStepCond, negOne, one); - upper = rewriter.create(loc, upper, adj); - } - Value diff = rewriter.create(loc, upper, lower); - Value disp = rewriter.create(loc, diff, step); - auto cmpOp = cast(c.compareOp); - Value up1 = rewriter.create(loc, disp, step); - Value noLoopCond = rewriter.create( - loc, arith::CmpIPredicate::sgt, up1, zero); - Value newUpper = - rewriter.create(loc, ty, noLoopCond, up1, zero); - - // 3) Rewrite the comparison (!=) and step operations (+1). - Value v1 = c.getCompareInduction(); - rewriter.setInsertionPoint(cmpOp); - Value newCmp = rewriter.create( - cmpOp.getLoc(), arith::CmpIPredicate::ne, v1, newUpper); - cmpOp->replaceAllUsesWith(ValueRange{newCmp}); - auto v2 = c.stepOp->getOperand( - c.stepIsAnAddOp() && c.shouldCommuteStepOp() ? 1 : 0); - rewriter.setInsertionPoint(c.stepOp); - auto newStep = rewriter.create(c.stepOp->getLoc(), v2, one); - c.stepOp->replaceAllUsesWith(ValueRange{newStep.getResult()}); - - // 4) Compute original induction value as a loop variant and replace the - // uses. `lower + step * i`. Careful to not replace the new induction. - if (!loop.getBodyRegion().empty()) { - Block *entry = &loop.getBodyRegion().front(); - rewriter.setInsertionPointToStart(entry); - Value induct = entry->getArgument(c.induction); - auto mul = rewriter.create(loc, induct, c.stepValue); - Value newInd; - if (c.stepIsAnAddOp()) - newInd = rewriter.create(loc, c.initialValue, mul); - else - newInd = rewriter.create(loc, c.initialValue, mul); - induct.replaceUsesWithIf(newInd, [&](OpOperand &opnd) { - auto *op = opnd.getOwner(); - return op != newStep.getOperation() && op != mul && - !isa(op); - }); - } - loop->setAttr(cudaq::opt::NormalizedLoopAttr, rewriter.getUnitAttr()); - - rewriter.finalizeRootUpdate(loop); - LLVM_DEBUG(llvm::dbgs() << "loop after normalization: " << loop << '\n'); - return success(); - } - - bool allowClosedInterval; - bool allowEarlyExit; -}; - class LoopNormalizePass : public cudaq::opt::impl::LoopNormalizeBase { public: diff --git a/lib/Optimizer/Transforms/LoopNormalizePatterns.inc b/lib/Optimizer/Transforms/LoopNormalizePatterns.inc new file mode 100644 index 0000000000..a147c78bd3 --- /dev/null +++ b/lib/Optimizer/Transforms/LoopNormalizePatterns.inc @@ -0,0 +1,169 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// These loop normalization patterns are used by the cc-loop-normalize pass +// and cc-loop-unroll pass + +// This file must be included after a `using namespace mlir;` as it uses bare +// identifiers from that namespace. + +// Return true if \p loop is not monotonic or it is an invariant loop. +// Normalization is to be done on any loop that is monotonic and not invariant +// (which includes loops that are already in counted form). +static bool isNotMonotonicOrInvariant(cudaq::cc::LoopOp loop, + bool allowClosedInterval, + bool allowEarlyExit) { + cudaq::opt::LoopComponents c; + return !cudaq::opt::isaMonotonicLoop(loop, allowEarlyExit, &c) || + (cudaq::opt::isaInvariantLoop(c, allowClosedInterval) && + !c.isLinearExpr()); +} + +namespace { +class LoopPat : public OpRewritePattern { +public: + explicit LoopPat(MLIRContext *ctx, bool aci, bool ab) + : OpRewritePattern(ctx), allowClosedInterval(aci), allowEarlyExit(ab) {} + + LogicalResult matchAndRewrite(cudaq::cc::LoopOp loop, + PatternRewriter &rewriter) const override { + if (loop->hasAttr(cudaq::opt::NormalizedLoopAttr) || + loop->hasAttr(cudaq::opt::DeadLoopAttr)) + return failure(); + if (isNotMonotonicOrInvariant(loop, allowClosedInterval, allowEarlyExit)) + return failure(); + + // loop is monotonic but not invariant. + LLVM_DEBUG(llvm::dbgs() << "loop before normalization: " << loop << '\n'); + auto componentsOpt = cudaq::opt::getLoopComponents(loop); + assert(componentsOpt && "loop must have components"); + auto c = *componentsOpt; + if (c.hasAlwaysTrueCondition()) { + loop->emitWarning("Loop condition is always true. This loop is not " + "supported in a kernel."); + return failure(); + } + + if (c.hasAlwaysFalseCondition()) { + rewriter.startRootUpdate(loop); + rewriter.replaceOpWithNewOp(c.compareOp, 0, 1); + loop->setAttr(cudaq::opt::DeadLoopAttr, rewriter.getUnitAttr()); + rewriter.finalizeRootUpdate(loop); + return success(); + } + auto loc = loop.getLoc(); + + // 1) Set initial value to 0. + auto ty = c.initialValue.getType(); + rewriter.startRootUpdate(loop); + auto createConstantOp = [&](std::int64_t val) -> Value { + return rewriter.create(loc, val, ty); + }; + auto zero = createConstantOp(0); + loop->setOperand(c.induction, zero); + + // 2) Compute the number of iterations as an invariant. `iterations = max(0, + // (upper - lower + step) / step)`. + Value upper = c.compareValue; + auto one = createConstantOp(1); + Value step = c.stepValue; + Value lower = c.initialValue; + if (!c.stepIsAnAddOp()) + step = rewriter.create(loc, zero, step); + if (c.isLinearExpr()) { + // Induction is part of a linear expression. Deal with the terms of the + // equation. `m` scales the step. `b` is an addend to the lower bound. + if (c.addendValue) { + if (c.negatedAddend) { + // `m * i - b`, u += `b`. + upper = rewriter.create(loc, upper, c.addendValue); + } else { + // `m * i + b`, u -= `b`. + upper = rewriter.create(loc, upper, c.addendValue); + } + } + if (c.minusOneMult) { + // `b - m * i` (b eliminated), multiply lower and step by `-1` (`m` + // follows). + auto negOne = createConstantOp(-1); + lower = rewriter.create(loc, lower, negOne); + step = rewriter.create(loc, step, negOne); + } + if (c.scaleValue) { + if (c.reciprocalScale) { + // `1/m * i + b` (b eliminated), multiply upper by `m`. + upper = rewriter.create(loc, upper, c.scaleValue); + } else { + // `m * i + b` (b eliminated), multiple lower and step by `m`. + lower = rewriter.create(loc, lower, c.scaleValue); + step = rewriter.create(loc, step, c.scaleValue); + } + } + } + if (!c.isClosedIntervalForm()) { + // Note: treating the step as a signed value to process countdown loops as + // well as countup loops. + Value negStepCond = rewriter.create( + loc, arith::CmpIPredicate::slt, step, zero); + auto negOne = createConstantOp(-1); + Value adj = + rewriter.create(loc, ty, negStepCond, negOne, one); + upper = rewriter.create(loc, upper, adj); + } + Value diff = rewriter.create(loc, upper, lower); + Value disp = rewriter.create(loc, diff, step); + auto cmpOp = cast(c.compareOp); + Value newUpper = rewriter.create(loc, disp, step); + if (cudaq::opt::isSignedPredicate(cmpOp.getPredicate())) { + Value noLoopCond = rewriter.create( + loc, arith::CmpIPredicate::sgt, newUpper, zero); + newUpper = + rewriter.create(loc, ty, noLoopCond, newUpper, zero); + } + + // 3) Rewrite the comparison (!=) and step operations (+1). + Value v1 = c.getCompareInduction(); + rewriter.setInsertionPoint(cmpOp); + Value newCmp = rewriter.create( + cmpOp.getLoc(), arith::CmpIPredicate::ne, v1, newUpper); + cmpOp->replaceAllUsesWith(ValueRange{newCmp}); + auto v2 = c.stepOp->getOperand( + c.stepIsAnAddOp() && c.shouldCommuteStepOp() ? 1 : 0); + rewriter.setInsertionPoint(c.stepOp); + auto newStep = rewriter.create(c.stepOp->getLoc(), v2, one); + c.stepOp->replaceAllUsesWith(ValueRange{newStep.getResult()}); + + // 4) Compute original induction value as a loop variant and replace the + // uses. `lower + step * i`. Careful to not replace the new induction. + if (!loop.getBodyRegion().empty()) { + Block *entry = &loop.getBodyRegion().front(); + rewriter.setInsertionPointToStart(entry); + Value induct = entry->getArgument(c.induction); + auto mul = rewriter.create(loc, induct, c.stepValue); + Value newInd; + if (c.stepIsAnAddOp()) + newInd = rewriter.create(loc, c.initialValue, mul); + else + newInd = rewriter.create(loc, c.initialValue, mul); + induct.replaceUsesWithIf(newInd, [&](OpOperand &opnd) { + auto *op = opnd.getOwner(); + return op != newStep.getOperation() && op != mul && + !isa(op); + }); + } + loop->setAttr(cudaq::opt::NormalizedLoopAttr, rewriter.getUnitAttr()); + + rewriter.finalizeRootUpdate(loop); + LLVM_DEBUG(llvm::dbgs() << "loop after normalization: " << loop << '\n'); + return success(); + } + + bool allowClosedInterval; + bool allowEarlyExit; +}; +} // namespace diff --git a/lib/Optimizer/Transforms/LoopUnroll.cpp b/lib/Optimizer/Transforms/LoopUnroll.cpp index b7f0053f26..b9ba7f137c 100644 --- a/lib/Optimizer/Transforms/LoopUnroll.cpp +++ b/lib/Optimizer/Transforms/LoopUnroll.cpp @@ -9,13 +9,14 @@ #include "LoopAnalysis.h" #include "PassDetails.h" #include "cudaq/Optimizer/Transforms/Passes.h" +#include "mlir/IR/Dominance.h" #include "mlir/IR/PatternMatch.h" #include "mlir/Transforms/GreedyPatternRewriteDriver.h" #include "mlir/Transforms/Passes.h" +#include "mlir/Transforms/RegionUtils.h" namespace cudaq::opt { #define GEN_PASS_DEF_LOOPUNROLL -#define GEN_PASS_DEF_UPDATEREGISTERNAMES #include "cudaq/Optimizer/Transforms/Passes.h.inc" } // namespace cudaq::opt @@ -23,202 +24,9 @@ namespace cudaq::opt { using namespace mlir; -inline std::pair findCloneRange(Block *first, Block *last) { - return {first->getNextNode(), last->getPrevNode()}; -} - -static std::size_t -unrollLoopByValue(cudaq::cc::LoopOp loop, - const cudaq::opt::LoopComponents &components) { - auto c = components.compareValue.getDefiningOp(); - return cast(c.getValue()).getInt(); -} - -static std::size_t unrollLoopByValue(cudaq::cc::LoopOp loop) { - auto components = cudaq::opt::getLoopComponents(loop); - return unrollLoopByValue(loop, *components); -} - -static bool exceedsThresholdValue(cudaq::cc::LoopOp loop, - std::size_t threshold) { - auto upperBound = unrollLoopByValue(loop); - return upperBound >= threshold; -} +#include "LoopUnrollPatterns.inc" namespace { - -/// We fully unroll a counted loop (so marked with the counted attribute) as -/// long as the number of iterations is constant and that constant is less than -/// the threshold value. -/// -/// Assumptions are made that the counted loop has a particular structural -/// layout as is consistent with the factory producing the counted loop. -/// -/// After this pass, all loops marked counted will be unrolled or marked -/// invariant. An invariant loop means the loop must execute exactly some -/// specific number of times, even if that number is only known at runtime. -struct UnrollCountedLoop : public OpRewritePattern { - explicit UnrollCountedLoop(MLIRContext *ctx, std::size_t t, bool sf, bool ab, - unsigned &p) - : OpRewritePattern(ctx), threshold(t), signalFailure(sf), allowBreak(ab), - progress(p) {} - - LogicalResult matchAndRewrite(cudaq::cc::LoopOp loop, - PatternRewriter &rewriter) const override { - // When the signalFailure flag is set, all loops are matched since that flag - // requires that all LoopOp operations be rewritten. Despite the setting of - // this flag, it may not be possible to fully unroll every LoopOp anyway. - // Check for cases that are clearly not going to be unrolled. - if (!allowBreak && !cudaq::opt::isaCountedLoop(loop)) { - if (signalFailure) - loop.emitOpError("not a simple counted loop"); - return failure(); - } - if (allowBreak && !cudaq::opt::isaConstantUpperBoundLoop(loop)) { - if (signalFailure) - loop.emitOpError("not a constant upper bound loop"); - return failure(); - } - if (exceedsThresholdValue(loop, threshold)) { - if (signalFailure) - loop.emitOpError("loop bounds exceed iteration threshold"); - return failure(); - } - - // At this point, we're ready to unroll the loop and replace it with a - // sequence of blocks. Each block will receive a block argument that is the - // iteration number. The original cc.loop will be replaced by a constant, - // the total number of iterations. - // TODO: Allow the threading of other block arguments to the result. - auto components = cudaq::opt::getLoopComponents(loop); - assert(components && "counted loop must have components"); - auto unrollBy = unrollLoopByValue(loop, *components); - if (components->isClosedIntervalForm()) - ++unrollBy; - Type inductionTy = loop.getOperands()[components->induction].getType(); - LLVM_DEBUG(llvm::dbgs() - << "unrolling loop by " << unrollBy << " iterations\n"); - auto loc = loop.getLoc(); - // Split the basic block in which this cc.loop appears. - auto *insBlock = rewriter.getInsertionBlock(); - auto insPos = rewriter.getInsertionPoint(); - auto *endBlock = rewriter.splitBlock(insBlock, insPos); - auto argTys = loop.getResultTypes(); - SmallVector argLocs(argTys.size(), loop.getLoc()); - endBlock->addArguments(argTys, argLocs); - rewriter.setInsertionPointToEnd(insBlock); - Value iterCount = getIntegerConstant(loc, inductionTy, 0, rewriter); - SmallVector locsRange(loop.getNumResults(), loc); - auto &bodyRegion = loop.getBodyRegion(); - SmallVector iterationOpers = loop.getOperands(); - auto setIterationOpers = [&](auto from) { - assert(iterationOpers.size() == from.size()); - for (auto i : llvm::enumerate(from)) - iterationOpers[i.index()] = i.value(); - }; - - // Make a constant number of copies of the body. - Block *contBlock = nullptr; - Value nextIterCount; - for (std::size_t i = 0u; i < unrollBy; ++i) { - // 1. Clone the while region. - rewriter.cloneRegionBefore(loop.getWhileRegion(), endBlock); - Block *whileBlock = insBlock->getNextNode(); - // 2. Clone the body region. - rewriter.cloneRegionBefore(bodyRegion, endBlock); - // Replace the ConditionOp in the while region clone with a direct branch. - // This makes the comparison there dead. DCE will delete any unneeded code - // associated with it. - auto cond = cast(whileBlock->getTerminator()); - rewriter.setInsertionPoint(cond); - rewriter.replaceOpWithNewOp(cond, whileBlock->getNextNode(), - cond.getResults()); - auto cloneRange = findCloneRange(insBlock, endBlock); - // 3. If the loop has a step region, clone it as well. Otherwise create an - // empty block to target as the next "continue" block. - if (loop.hasStep()) { - contBlock = endBlock->getPrevNode(); - rewriter.cloneRegionBefore(loop.getStepRegion(), endBlock); - contBlock = contBlock->getNextNode(); - } else { - contBlock = rewriter.createBlock(endBlock, argTys, argLocs); - } - // Replace any continue and (possibly) break ops in the body region. They - // are repalced with branches to the continue block or exit block, resp. - for (Block *b = cloneRange.first; b != contBlock; b = b->getNextNode()) { - auto *term = b->getTerminator(); - if (auto cont = dyn_cast(term)) { - auto termOpers = cont.getOperands(); - rewriter.setInsertionPoint(cont); - rewriter.replaceOpWithNewOp(cont, contBlock, termOpers); - } - if (allowBreak) { - if (auto brk = dyn_cast(term)) { - auto termOpers = brk.getOperands(); - rewriter.setInsertionPoint(brk); - rewriter.replaceOpWithNewOp(brk, endBlock, termOpers); - } - } - } - // If there was a step region, its entry block is the continue block. - // However, it may have multiple exit blocks. Thread each of these to a - // merge block. The continue block is updated to this new empty merge - // block. - if (loop.hasStep()) { - Block *mergeBlock = rewriter.createBlock(endBlock, argTys, argLocs); - for (Block *b = contBlock; b != mergeBlock; b = b->getNextNode()) - if (auto cont = dyn_cast(b->getTerminator())) { - auto termOpers = cont.getOperands(); - rewriter.setInsertionPoint(cont); - rewriter.replaceOpWithNewOp(cont, mergeBlock, - termOpers); - } - contBlock = mergeBlock; - } - // At this point, the continue block is a new, empty block. Generate the - // next iteration number in this continue block. - rewriter.setInsertionPointToEnd(contBlock); - nextIterCount = getIntegerConstant(loc, inductionTy, i + 1, rewriter); - rewriter.setInsertionPointToEnd(insBlock); - // Propagate the previous iteration number into the new block. This makes - // any unneeded computation dead. DCE will clean that up as well. - iterationOpers[components->induction] = iterCount; - rewriter.create(loc, cloneRange.first, iterationOpers); - // Bookkeeping for the next iteration, which uses the new continue block, - // `conBlock`, and its arguments. - setIterationOpers(contBlock->getArguments()); - iterCount = nextIterCount; - insBlock = contBlock; - } - - // Finish up the last block. - rewriter.setInsertionPointToEnd(insBlock); - if (contBlock) { - iterationOpers[components->induction] = nextIterCount; - setIterationOpers(contBlock->getArguments()); - } - [[maybe_unused]] auto lastBranch = - rewriter.create(loc, endBlock, iterationOpers); - rewriter.replaceOp(loop, endBlock->getArguments()); - - LLVM_DEBUG(llvm::dbgs() << "after unrolling a loop:\n"; - lastBranch->getParentOfType().dump()); - progress++; - return success(); - } - - static Value getIntegerConstant(Location loc, Type ty, std::int64_t val, - PatternRewriter &rewriter) { - auto attr = rewriter.getIntegerAttr(ty, val); - return rewriter.create(loc, ty, attr); - } - - std::size_t threshold; - bool signalFailure; - bool allowBreak; - unsigned &progress; -}; - /// The loop unrolling pass will fully unroll a `cc::LoopOp` when the loop is /// known to always execute a constant number of iterations. That is, the loop /// is a counted loop. (A threshold value can be used to bound the legal range @@ -262,53 +70,15 @@ class LoopUnrollPass : public cudaq::opt::impl::LoopUnrollBase { static unsigned countLoopOps(Operation *op) { unsigned result = 0; - op->walk([&](cudaq::cc::LoopOp loop) { result++; }); + op->walk([&](cudaq::cc::LoopOp loop) { + if (!loop->hasAttr(cudaq::opt::DeadLoopAttr)) + result++; + }); LLVM_DEBUG(llvm::dbgs() << "Total number of loops: " << result << '\n'); return result; } }; -/// After unrolling the loops, there may be duplicate registerName attributes in -/// use. This pass will assign them unique names by appending a counter. -class UpdateRegisterNamesPass - : public cudaq::opt::impl::UpdateRegisterNamesBase< - UpdateRegisterNamesPass> { -public: - using UpdateRegisterNamesBase::UpdateRegisterNamesBase; - - void runOnOperation() override { - auto *mod = getOperation(); - - // First save the op's that contain a registerName attribute - DenseMap> regOps; - mod->walk([&](mlir::Operation *walkOp) { - if (auto prevAttr = walkOp->getAttr("registerName")) { - auto registerName = prevAttr.cast().getValue(); - regOps[registerName].push_back(walkOp); - } - return WalkResult::advance(); - }); - - // Now apply new labels, appending a counter if necessary - for (auto &[registerName, opVec] : regOps) { - if (opVec.size() == 1) - continue; // don't rename individual qubit measurements - auto strLen = std::to_string(opVec.size()).size(); - int bit = 0; - for (auto ®Op : opVec) - if (auto prevAttr = regOp->getAttr("registerName")) { - auto suffix = std::to_string(bit++); - if (suffix.size() < strLen) - suffix = std::string(strLen - suffix.size(), '0') + suffix; - // Note Quantinuum can't support a ":" delimiter, so use '%' - auto newAttr = OpBuilder(&getContext()) - .getStringAttr(registerName + "%" + suffix); - regOp->setAttr("registerName", newAttr); - } - } - } -}; - /// Unrolling pass pipeline command-line options. These options are similar to /// the LoopUnroll pass options, but have different default settings. struct UnrollPipelineOptions diff --git a/lib/Optimizer/Transforms/LoopUnrollPatterns.inc b/lib/Optimizer/Transforms/LoopUnrollPatterns.inc new file mode 100644 index 0000000000..0db404d050 --- /dev/null +++ b/lib/Optimizer/Transforms/LoopUnrollPatterns.inc @@ -0,0 +1,217 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// These patterns are used by classical-optimization pass and cc-loop-unroll +// pass. + +// This file must be included after a `using namespace mlir;` as it uses bare +// identifiers from that namespace. + +inline std::pair findCloneRange(Block *first, Block *last) { + return {first->getNextNode(), last->getPrevNode()}; +} + +static std::size_t +unrollLoopByValue(cudaq::cc::LoopOp loop, + const cudaq::opt::LoopComponents &components) { + auto c = components.compareValue.getDefiningOp(); + if (loop->hasAttr(cudaq::opt::NormalizedLoopAttr)) + return cast(c.getValue()).getInt(); + if (components.hasAlwaysFalseCondition()) + return 0; + auto resultOpt = components.getIterationsConstant(); + assert(resultOpt.has_value() && "must be counted loop"); + return *resultOpt; +} + +static bool exceedsThresholdValue(cudaq::cc::LoopOp loop, + std::size_t threshold) { + auto components = cudaq::opt::getLoopComponents(loop); + if (components->hasAlwaysTrueCondition()) { + loop->emitWarning("Loop condition is always true. This loop is not " + "supported in a kernel."); + return true; + } + auto upperBound = unrollLoopByValue(loop, *components); + return upperBound >= threshold; +} + +namespace { + +/// We fully unroll a counted loop (so marked with the counted attribute) as +/// long as the number of iterations is constant and that constant is less than +/// the threshold value. +/// +/// Assumptions are made that the counted loop has a particular structural +/// layout as is consistent with the factory producing the counted loop. +/// +/// After this pass, all loops marked counted will be unrolled or marked +/// invariant. An invariant loop means the loop must execute exactly some +/// specific number of times, even if that number is only known at runtime. +struct UnrollCountedLoop : public OpRewritePattern { + explicit UnrollCountedLoop(MLIRContext *ctx, std::size_t t, bool sf, bool ab, + unsigned &p) + : OpRewritePattern(ctx), threshold(t), signalFailure(sf), allowBreak(ab), + progress(p) {} + + LogicalResult matchAndRewrite(cudaq::cc::LoopOp loop, + PatternRewriter &rewriter) const override { + // When the signalFailure flag is set, all loops are matched since that flag + // requires that all LoopOp operations be rewritten. Despite the setting of + // this flag, it may not be possible to fully unroll every LoopOp anyway. + // Check for cases that are clearly not going to be unrolled. + if (loop->hasAttr(cudaq::opt::DeadLoopAttr)) + return failure(); + if (!allowBreak && !cudaq::opt::isaCountedLoop(loop)) { + if (signalFailure) + loop.emitOpError("not a simple counted loop"); + return failure(); + } + if (allowBreak && !cudaq::opt::isaConstantUpperBoundLoop(loop)) { + if (signalFailure) + loop.emitOpError("not a constant upper bound loop"); + return failure(); + } + if (exceedsThresholdValue(loop, threshold)) { + if (signalFailure) + loop.emitOpError("loop bounds exceed iteration threshold"); + return failure(); + } + + // At this point, we're ready to unroll the loop and replace it with a + // sequence of blocks. Each block will receive a block argument that is the + // iteration number. The original cc.loop will be replaced by a constant, + // the total number of iterations. + // TODO: Allow the threading of other block arguments to the result. + auto components = cudaq::opt::getLoopComponents(loop); + assert(components && "counted loop must have components"); + auto unrollBy = unrollLoopByValue(loop, *components); + Type inductionTy = loop.getOperands()[components->induction].getType(); + LLVM_DEBUG(llvm::dbgs() + << "unrolling loop by " << unrollBy << " iterations\n"); + auto loc = loop.getLoc(); + // Split the basic block in which this cc.loop appears. + auto *insBlock = rewriter.getInsertionBlock(); + auto insPos = rewriter.getInsertionPoint(); + auto *endBlock = rewriter.splitBlock(insBlock, insPos); + auto argTys = loop.getResultTypes(); + SmallVector argLocs(argTys.size(), loop.getLoc()); + endBlock->addArguments(argTys, argLocs); + rewriter.setInsertionPointToEnd(insBlock); + Value iterCount = getIntegerConstant(loc, inductionTy, 0, rewriter); + SmallVector locsRange(loop.getNumResults(), loc); + auto &bodyRegion = loop.getBodyRegion(); + SmallVector iterationOpers = loop.getOperands(); + auto setIterationOpers = [&](auto from) { + assert(iterationOpers.size() == from.size()); + for (auto i : llvm::enumerate(from)) + iterationOpers[i.index()] = i.value(); + }; + + // Make a constant number of copies of the body. + Block *contBlock = nullptr; + Value nextIterCount; + for (std::size_t i = 0u; i < unrollBy; ++i) { + // 1. Clone the while region. + rewriter.cloneRegionBefore(loop.getWhileRegion(), endBlock); + Block *whileBlock = insBlock->getNextNode(); + // 2. Clone the body region. + rewriter.cloneRegionBefore(bodyRegion, endBlock); + // Replace the ConditionOp in the while region clone with a direct branch. + // This makes the comparison there dead. DCE will delete any unneeded code + // associated with it. + auto cond = cast(whileBlock->getTerminator()); + rewriter.setInsertionPoint(cond); + rewriter.replaceOpWithNewOp(cond, whileBlock->getNextNode(), + cond.getResults()); + auto cloneRange = findCloneRange(insBlock, endBlock); + // 3. If the loop has a step region, clone it as well. Otherwise create an + // empty block to target as the next "continue" block. + if (loop.hasStep()) { + contBlock = endBlock->getPrevNode(); + rewriter.cloneRegionBefore(loop.getStepRegion(), endBlock); + contBlock = contBlock->getNextNode(); + } else { + contBlock = rewriter.createBlock(endBlock, argTys, argLocs); + } + // Replace any continue and (possibly) break ops in the body region. They + // are replaced with branches to the continue block or exit block, resp. + for (Block *b = cloneRange.first; b != contBlock; b = b->getNextNode()) { + auto *term = b->getTerminator(); + if (auto cont = dyn_cast(term)) { + auto termOpers = cont.getOperands(); + rewriter.setInsertionPoint(cont); + rewriter.replaceOpWithNewOp(cont, contBlock, termOpers); + } + if (allowBreak) { + if (auto brk = dyn_cast(term)) { + auto termOpers = brk.getOperands(); + rewriter.setInsertionPoint(brk); + rewriter.replaceOpWithNewOp(brk, endBlock, termOpers); + } + } + } + // If there was a step region, its entry block is the continue block. + // However, it may have multiple exit blocks. Thread each of these to a + // merge block. The continue block is updated to this new empty merge + // block. + if (loop.hasStep()) { + Block *mergeBlock = rewriter.createBlock(endBlock, argTys, argLocs); + for (Block *b = contBlock; b != mergeBlock; b = b->getNextNode()) + if (auto cont = dyn_cast(b->getTerminator())) { + auto termOpers = cont.getOperands(); + rewriter.setInsertionPoint(cont); + rewriter.replaceOpWithNewOp(cont, mergeBlock, + termOpers); + } + contBlock = mergeBlock; + } + // At this point, the continue block is a new, empty block. Generate the + // next iteration number in this continue block. + rewriter.setInsertionPointToEnd(contBlock); + nextIterCount = getIntegerConstant(loc, inductionTy, i + 1, rewriter); + rewriter.setInsertionPointToEnd(insBlock); + // Propagate the previous iteration number into the new block. This makes + // any unneeded computation dead. DCE will clean that up as well. + iterationOpers[components->induction] = iterCount; + rewriter.create(loc, cloneRange.first, iterationOpers); + // Bookkeeping for the next iteration, which uses the new continue block, + // `conBlock`, and its arguments. + setIterationOpers(contBlock->getArguments()); + iterCount = nextIterCount; + insBlock = contBlock; + } + + // Finish up the last block. + rewriter.setInsertionPointToEnd(insBlock); + if (contBlock) { + iterationOpers[components->induction] = nextIterCount; + setIterationOpers(contBlock->getArguments()); + } + [[maybe_unused]] auto lastBranch = + rewriter.create(loc, endBlock, iterationOpers); + rewriter.replaceOp(loop, endBlock->getArguments()); + + LLVM_DEBUG(llvm::dbgs() << "after unrolling a loop:\n"; + lastBranch->getParentOfType().dump()); + progress++; + return success(); + } + + static Value getIntegerConstant(Location loc, Type ty, std::int64_t val, + PatternRewriter &rewriter) { + auto attr = rewriter.getIntegerAttr(ty, val); + return rewriter.create(loc, ty, attr); + } + + std::size_t threshold; + bool signalFailure; + bool allowBreak; + unsigned &progress; +}; +} // namespace diff --git a/lib/Optimizer/Transforms/LowerToCFG.cpp b/lib/Optimizer/Transforms/LowerToCFG.cpp index fb050fabff..6431153542 100644 --- a/lib/Optimizer/Transforms/LowerToCFG.cpp +++ b/lib/Optimizer/Transforms/LowerToCFG.cpp @@ -20,6 +20,8 @@ using namespace mlir; +#include "LowerToCFGPatterns.inc" + namespace { class RewriteScope : public OpRewritePattern { public: @@ -278,80 +280,6 @@ class RewriteLoop : public OpRewritePattern { } }; -class RewriteIf : public OpRewritePattern { -public: - using OpRewritePattern::OpRewritePattern; - - /// Rewrites an if construct like - /// ```mlir - /// (0) - /// quake.if %cond { - /// (1) - /// } else { - /// (2) - /// } - /// (3) - /// ``` - /// to a CFG like - /// ```mlir - /// (0) - /// cf.cond_br %cond, ^bb1, ^bb2 - /// ^bb1: - /// (1) - /// cf.br ^bb3 - /// ^bb2: - /// (2) - /// cf.br ^bb3 - /// ^bb3: - /// (3) - /// ``` - LogicalResult matchAndRewrite(cudaq::cc::IfOp ifOp, - PatternRewriter &rewriter) const override { - auto loc = ifOp.getLoc(); - auto *initBlock = rewriter.getInsertionBlock(); - auto initPos = rewriter.getInsertionPoint(); - auto *endBlock = rewriter.splitBlock(initBlock, initPos); - if (ifOp.getNumResults() != 0) { - Block *continueBlock = rewriter.createBlock( - endBlock, ifOp.getResultTypes(), - SmallVector(ifOp.getNumResults(), loc)); - rewriter.create(loc, endBlock); - endBlock = continueBlock; - } - auto *thenBlock = &ifOp.getThenRegion().front(); - bool hasElse = !ifOp.getElseRegion().empty(); - auto *elseBlock = hasElse ? &ifOp.getElseRegion().front() : endBlock; - updateBodyBranches(&ifOp.getThenRegion(), rewriter, endBlock); - updateBodyBranches(&ifOp.getElseRegion(), rewriter, endBlock); - rewriter.inlineRegionBefore(ifOp.getThenRegion(), endBlock); - if (hasElse) - rewriter.inlineRegionBefore(ifOp.getElseRegion(), endBlock); - rewriter.setInsertionPointToEnd(initBlock); - rewriter.create(loc, ifOp.getCondition(), thenBlock, - ifOp.getLinearArgs(), elseBlock, - ifOp.getLinearArgs()); - rewriter.replaceOp(ifOp, endBlock->getArguments()); - return success(); - } - - // Replace all the ContinueOp in the body region with branches to the correct - // basic blocks. - void updateBodyBranches(Region *bodyRegion, PatternRewriter &rewriter, - Block *continueBlock) const { - // Walk body region and replace all continue and break ops. - for (Block &block : *bodyRegion) { - auto *terminator = block.getTerminator(); - if (auto cont = dyn_cast(terminator)) { - rewriter.setInsertionPointToEnd(&block); - LLVM_DEBUG(llvm::dbgs() << "replacing " << *terminator << '\n'); - rewriter.replaceOpWithNewOp(cont, continueBlock, - cont.getOperands()); - } - // Other ad-hoc control flow in the region need not be rewritten. - } - } -}; - class RewriteReturn : public OpRewritePattern { public: using OpRewritePattern::OpRewritePattern; diff --git a/lib/Optimizer/Transforms/LowerToCFGPatterns.inc b/lib/Optimizer/Transforms/LowerToCFGPatterns.inc new file mode 100644 index 0000000000..a449d6df69 --- /dev/null +++ b/lib/Optimizer/Transforms/LowerToCFGPatterns.inc @@ -0,0 +1,104 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ +// These patterns are used by the lower-to-cfg pass and cc-loop-unroll pass. + +// This file must be included after a `using namespace mlir;` as it uses bare +// identifiers from that namespace. + +namespace { +class RewriteIf : public OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + explicit RewriteIf(MLIRContext *ctx) + : OpRewritePattern(ctx), rewriteOnlyIfConst(false) {} + + RewriteIf(MLIRContext *ctx, bool rewriteOnlyIfConst) + : OpRewritePattern(ctx), rewriteOnlyIfConst(rewriteOnlyIfConst) {} + + /// Rewrites an if construct like + /// ```mlir + /// (0) + /// quake.if %cond { + /// (1) + /// } else { + /// (2) + /// } + /// (3) + /// ``` + /// to a CFG like + /// ```mlir + /// (0) + /// cf.cond_br %cond, ^bb1, ^bb2 + /// ^bb1: + /// (1) + /// cf.br ^bb3 + /// ^bb2: + /// (2) + /// cf.br ^bb3 + /// ^bb3: + /// (3) + /// ``` + LogicalResult matchAndRewrite(cudaq::cc::IfOp ifOp, + PatternRewriter &rewriter) const override { + // Bail out on non-constant conditions if we just need to + // const-prop if($const). + if (rewriteOnlyIfConst) { + auto cond = ifOp.getCondition(); + if (!isa_and_present(cond.getDefiningOp())) + return failure(); + } + + auto loc = ifOp.getLoc(); + auto *initBlock = rewriter.getInsertionBlock(); + auto initPos = rewriter.getInsertionPoint(); + auto *endBlock = rewriter.splitBlock(initBlock, initPos); + if (ifOp.getNumResults() != 0) { + Block *continueBlock = rewriter.createBlock( + endBlock, ifOp.getResultTypes(), + SmallVector(ifOp.getNumResults(), loc)); + rewriter.create(loc, endBlock); + endBlock = continueBlock; + } + auto *thenBlock = &ifOp.getThenRegion().front(); + bool hasElse = !ifOp.getElseRegion().empty(); + auto *elseBlock = hasElse ? &ifOp.getElseRegion().front() : endBlock; + updateBodyBranches(&ifOp.getThenRegion(), rewriter, endBlock); + updateBodyBranches(&ifOp.getElseRegion(), rewriter, endBlock); + rewriter.inlineRegionBefore(ifOp.getThenRegion(), endBlock); + if (hasElse) + rewriter.inlineRegionBefore(ifOp.getElseRegion(), endBlock); + rewriter.setInsertionPointToEnd(initBlock); + rewriter.create(loc, ifOp.getCondition(), thenBlock, + ifOp.getLinearArgs(), elseBlock, + ifOp.getLinearArgs()); + rewriter.replaceOp(ifOp, endBlock->getArguments()); + return success(); + } + + // Replace all the ContinueOp in the body region with branches to the correct + // basic blocks. + void updateBodyBranches(Region *bodyRegion, PatternRewriter &rewriter, + Block *continueBlock) const { + // Walk body region and replace all continue and break ops. + for (Block &block : *bodyRegion) { + auto *terminator = block.getTerminator(); + if (auto cont = dyn_cast(terminator)) { + rewriter.setInsertionPointToEnd(&block); + LLVM_DEBUG(llvm::dbgs() << "replacing " << *terminator << '\n'); + rewriter.replaceOpWithNewOp(cont, continueBlock, + cont.getOperands()); + } + // Other ad-hoc control flow in the region need not be rewritten. + } + } + +private: + bool rewriteOnlyIfConst = false; +}; +} // namespace diff --git a/lib/Optimizer/Transforms/UpdateRegisterNames.cpp b/lib/Optimizer/Transforms/UpdateRegisterNames.cpp new file mode 100644 index 0000000000..5bf00d241f --- /dev/null +++ b/lib/Optimizer/Transforms/UpdateRegisterNames.cpp @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "LoopAnalysis.h" +#include "PassDetails.h" +#include "cudaq/Optimizer/Transforms/Passes.h" +#include "mlir/IR/Dominance.h" +#include "mlir/IR/PatternMatch.h" +#include "mlir/Transforms/GreedyPatternRewriteDriver.h" +#include "mlir/Transforms/Passes.h" +#include "mlir/Transforms/RegionUtils.h" + +namespace cudaq::opt { +#define GEN_PASS_DEF_UPDATEREGISTERNAMES +#include "cudaq/Optimizer/Transforms/Passes.h.inc" +} // namespace cudaq::opt + +#define DEBUG_TYPE "update-register-names" + +using namespace mlir; + +namespace { +/// After unrolling the loops, there may be duplicate registerName attributes in +/// use. This pass will assign them unique names by appending a counter. +class UpdateRegisterNamesPass + : public cudaq::opt::impl::UpdateRegisterNamesBase< + UpdateRegisterNamesPass> { +public: + using UpdateRegisterNamesBase::UpdateRegisterNamesBase; + + void runOnOperation() override { + auto *mod = getOperation(); + + // First save the op's that contain a registerName attribute + DenseMap> regOps; + mod->walk([&](mlir::Operation *walkOp) { + if (auto prevAttr = walkOp->getAttr("registerName")) { + auto registerName = prevAttr.cast().getValue(); + regOps[registerName].push_back(walkOp); + } + return WalkResult::advance(); + }); + + // Now apply new labels, appending a counter if necessary + for (auto &[registerName, opVec] : regOps) { + if (opVec.size() == 1) + continue; // don't rename individual qubit measurements + auto strLen = std::to_string(opVec.size()).size(); + int bit = 0; + for (auto ®Op : opVec) + if (auto prevAttr = regOp->getAttr("registerName")) { + auto suffix = std::to_string(bit++); + if (suffix.size() < strLen) + suffix = std::string(strLen - suffix.size(), '0') + suffix; + // Note Quantinuum can't support a ":" delimiter, so use '%' + auto newAttr = OpBuilder(&getContext()) + .getStringAttr(registerName + "%" + suffix); + regOp->setAttr("registerName", newAttr); + } + } + } +}; +} // namespace diff --git a/lib/Optimizer/Transforms/WriteAfterWriteElimination.cpp b/lib/Optimizer/Transforms/WriteAfterWriteElimination.cpp index 6ab5f2272a..9f9b1d571d 100644 --- a/lib/Optimizer/Transforms/WriteAfterWriteElimination.cpp +++ b/lib/Optimizer/Transforms/WriteAfterWriteElimination.cpp @@ -27,121 +27,9 @@ namespace cudaq::opt { using namespace mlir; -namespace { -/// Remove stores followed by a store to the same pointer -/// if the pointer is not used in between. -/// ``` -/// cc.store %c0_i64, %1 : !cc.ptr -/// // no use of %1 until next line -/// cc.store %0, %1 : !cc.ptr -/// ─────────────────────────────────────────── -/// cc.store %0, %1 : !cc.ptr -/// ``` -class SimplifyWritesAnalysis { -public: - SimplifyWritesAnalysis(DominanceInfo &di, Operation *op) : dom(di) { - for (auto ®ion : op->getRegions()) - for (auto &b : region) - collectBlockInfo(&b); - } - - /// Remove stores followed by a store to the same pointer if the pointer is - /// not used in between, using collected block info. - void removeOverriddenStores() { - SmallVector toErase; - - for (const auto &[block, ptrToStores] : blockInfo) { - for (const auto &[ptr, stores] : ptrToStores) { - if (stores.size() > 1) { - auto replacement = stores.back(); - for (auto *store : stores) { - if (isReplacement(ptr, store, replacement)) { - LLVM_DEBUG(llvm::dbgs() << "replacing store " << *store - << " by: " << *replacement << '\n'); - toErase.push_back(store); - } - } - } - } - } - - for (auto *op : toErase) - op->erase(); - } - -private: - /// Detect if value is used in the op or its nested blocks. - bool isReplacement(Operation *ptr, Operation *store, - Operation *replacement) const { - if (store == replacement) - return false; - - // Check that there are no non-store uses dominated by the store and - // not dominated by the replacement, i.e. only uses between the two - // stores are other stores to the same pointer. - for (auto *user : ptr->getUsers()) { - if (user != store && user != replacement) { - if (!isStoreToPtr(user, ptr) && dom.dominates(store, user) && - !dom.dominates(replacement, user)) { - LLVM_DEBUG(llvm::dbgs() << "store " << replacement - << " is used before: " << store << '\n'); - return false; - } - } - } - return true; - } - - /// Detects a store to the pointer. - static bool isStoreToPtr(Operation *op, Operation *ptr) { - return isa_and_present(op) && - (dyn_cast(op).getPtrvalue().getDefiningOp() == - ptr); - } - - /// Collect all stores to a pointer for a block. - void collectBlockInfo(Block *block) { - for (auto &op : *block) { - for (auto ®ion : op.getRegions()) - for (auto &b : region) - collectBlockInfo(&b); - - if (auto store = dyn_cast(&op)) { - auto ptr = store.getPtrvalue().getDefiningOp(); - if (isStoreToStack(store)) { - auto &[b, ptrToStores] = blockInfo.FindAndConstruct(block); - auto &[p, stores] = ptrToStores.FindAndConstruct(ptr); - stores.push_back(&op); - } - } - } - } - - /// Detect stores to stack locations, for example: - /// ``` - /// %1 = cc.alloca !cc.array - /// - /// %2 = cc.cast %1 : (!cc.ptr>) -> !cc.ptr - /// cc.store %c0_i64, %2 : !cc.ptr - /// - /// %3 = cc.compute_ptr %1[1] : (!cc.ptr>) -> !cc.ptr - /// cc.store %c0_i64, %3 : !cc.ptr - /// ``` - static bool isStoreToStack(cudaq::cc::StoreOp store) { - auto ptrOp = store.getPtrvalue(); - if (auto cast = ptrOp.getDefiningOp()) - ptrOp = cast.getOperand(); - - if (auto computePtr = ptrOp.getDefiningOp()) - ptrOp = computePtr.getBase(); - - return isa_and_present(ptrOp.getDefiningOp()); - } - - DominanceInfo &dom; - DenseMap>> blockInfo; -}; +#include "WriteAfterWriteEliminationPatterns.inc" +namespace { class WriteAfterWriteEliminationPass : public cudaq::opt::impl::WriteAfterWriteEliminationBase< WriteAfterWriteEliminationPass> { diff --git a/lib/Optimizer/Transforms/WriteAfterWriteEliminationPatterns.inc b/lib/Optimizer/Transforms/WriteAfterWriteEliminationPatterns.inc new file mode 100644 index 0000000000..f6fbf30d3f --- /dev/null +++ b/lib/Optimizer/Transforms/WriteAfterWriteEliminationPatterns.inc @@ -0,0 +1,129 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// These patterns are used by the write-after-write-elimination and +// cc-loop-unroll passes. + +// This file must be included after a `using namespace mlir;` as it uses bare +// identifiers from that namespace. + +namespace { +/// Remove stores followed by a store to the same pointer +/// if the pointer is not used in between. +/// ``` +/// cc.store %c0_i64, %1 : !cc.ptr +/// // no use of %1 until next line +/// cc.store %0, %1 : !cc.ptr +/// ─────────────────────────────────────────── +/// cc.store %0, %1 : !cc.ptr +/// ``` +class SimplifyWritesAnalysis { +public: + SimplifyWritesAnalysis(DominanceInfo &di, Operation *op) : dom(di) { + for (auto ®ion : op->getRegions()) + for (auto &b : region) + collectBlockInfo(&b); + } + + /// Remove stores followed by a store to the same pointer if the pointer is + /// not used in between, using collected block info. + void removeOverriddenStores() { + SmallVector toErase; + + for (const auto &[block, ptrToStores] : blockInfo) { + for (const auto &[ptr, stores] : ptrToStores) { + if (stores.size() > 1) { + auto replacement = stores.back(); + for (auto *store : stores) { + if (isReplacement(ptr, store, replacement)) { + LLVM_DEBUG(llvm::dbgs() << "replacing store " << *store + << " by: " << *replacement << '\n'); + toErase.push_back(store); + } + } + } + } + } + + for (auto *op : toErase) + op->erase(); + } + +private: + /// Detect if value is used in the op or its nested blocks. + bool isReplacement(Operation *ptr, Operation *store, + Operation *replacement) const { + if (store == replacement) + return false; + + // Check that there are no non-store uses dominated by the store and + // not dominated by the replacement, i.e. only uses between the two + // stores are other stores to the same pointer. + for (auto *user : ptr->getUsers()) { + if (user != store && user != replacement) { + if (!isStoreToPtr(user, ptr) && dom.dominates(store, user) && + !dom.dominates(replacement, user)) { + LLVM_DEBUG(llvm::dbgs() << "store " << replacement + << " is used before: " << store << '\n'); + return false; + } + } + } + return true; + } + + /// Detects a store to the pointer. + static bool isStoreToPtr(Operation *op, Operation *ptr) { + return isa_and_present(op) && + (dyn_cast(op).getPtrvalue().getDefiningOp() == + ptr); + } + + /// Collect all stores to a pointer for a block. + void collectBlockInfo(Block *block) { + for (auto &op : *block) { + for (auto ®ion : op.getRegions()) + for (auto &b : region) + collectBlockInfo(&b); + + if (auto store = dyn_cast(&op)) { + auto ptr = store.getPtrvalue().getDefiningOp(); + if (isStoreToStack(store)) { + auto &[b, ptrToStores] = blockInfo.FindAndConstruct(block); + auto &[p, stores] = ptrToStores.FindAndConstruct(ptr); + stores.push_back(&op); + } + } + } + } + + /// Detect stores to stack locations, for example: + /// ``` + /// %1 = cc.alloca !cc.array + /// + /// %2 = cc.cast %1 : (!cc.ptr>) -> !cc.ptr + /// cc.store %c0_i64, %2 : !cc.ptr + /// + /// %3 = cc.compute_ptr %1[1] : (!cc.ptr>) -> !cc.ptr + /// cc.store %c0_i64, %3 : !cc.ptr + /// ``` + static bool isStoreToStack(cudaq::cc::StoreOp store) { + auto ptrOp = store.getPtrvalue(); + if (auto cast = ptrOp.getDefiningOp()) + ptrOp = cast.getOperand(); + + if (auto computePtr = ptrOp.getDefiningOp()) + ptrOp = computePtr.getBase(); + + return isa_and_present(ptrOp.getDefiningOp()); + } + + DominanceInfo &dom; + DenseMap>> blockInfo; +}; +} // namespace diff --git a/python/cudaq/kernel/analysis.py b/python/cudaq/kernel/analysis.py index c68e47c7ab..3f8f86c145 100644 --- a/python/cudaq/kernel/analysis.py +++ b/python/cudaq/kernel/analysis.py @@ -177,7 +177,7 @@ def visit_Call(self, node): if name not in globalAstRegistry: raise RuntimeError( - f"{name} is not a valid kernel to call ({'.'.join(moduleNames)})." + f"{name} is not a valid kernel to call ({'.'.join(moduleNames)}). Registry: {globalAstRegistry}" ) self.depKernels[name] = globalAstRegistry[name] diff --git a/python/cudaq/kernel/ast_bridge.py b/python/cudaq/kernel/ast_bridge.py index 3463b4a1e0..499d49e8d6 100644 --- a/python/cudaq/kernel/ast_bridge.py +++ b/python/cudaq/kernel/ast_bridge.py @@ -658,6 +658,73 @@ def __insertDbgStmt(self, value, dbgStmt): func.CallOp(printFunc, [strLit, value]) return + def __get_vector_size(self, vector): + """ + Get the size of a vector or array type. + + Args: + vector: MLIR Value of vector/array type + + Returns: + MLIR Value containing the size as an integer + """ + if cc.StdvecType.isinstance(vector.type): + return cc.StdvecSizeOp(self.getIntegerType(), vector).result + return self.getConstantInt( + cc.ArrayType.getSize(cc.PointerType.getElementType(vector.type))) + + def __load_vector_element(self, vector, index): + """ + Load an element from a vector or array at the given index. + + Args: + vector: MLIR Value of vector/array type + index: MLIR Value containing integer index + + Returns: + MLIR Value containing the loaded element + """ + if cc.StdvecType.isinstance(vector.type): + data_ptr = cc.StdvecDataOp( + cc.PointerType.get( + self.ctx, + cc.ArrayType.get(self.ctx, + cc.StdvecType.getElementType( + vector.type))), vector).result + return cc.LoadOp( + cc.ComputePtrOp( + cc.PointerType.get( + self.ctx, + cc.StdvecType.getElementType(vector.type)), data_ptr, + [index], DenseI32ArrayAttr.get([kDynamicPtrIndex]))).result + return cc.LoadOp( + cc.ComputePtrOp( + cc.PointerType.get( + self.ctx, + cc.ArrayType.getElementType( + cc.PointerType.getElementType(vector.type))), vector, + [index], DenseI32ArrayAttr.get([kDynamicPtrIndex]))).result + + def __get_superior_type(self, a, b): + """ + Get the superior numeric type between two MLIR Values. + F64 > F32 > Integer, with integers promoting to the wider width. + + Args: + a: First MLIR Value + b: Second MLIR Value + + Returns: + MLIR Type representing the superior type + """ + if F64Type.isinstance(a.type) or F64Type.isinstance(b.type): + return F64Type.get() + if F32Type.isinstance(a.type) or F32Type.isinstance(b.type): + return F32Type.get() + return self.getIntegerType( + max(IntegerType(a.type).width, + IntegerType(b.type).width)) + def convertArithmeticToSuperiorType(self, values, type): """ Assuming all values provided are arithmetic, convert each one to the @@ -3536,6 +3603,50 @@ def visit_Compare(self, node): right).result) return + if isinstance(op, (ast.In, ast.NotIn)): + right_val = right + left_val = left + + # Type validation and vector initialization + if not (cc.StdvecType.isinstance(right_val.type) or + cc.ArrayType.isinstance(right_val.type)): + self.emitFatalError( + "Right operand must be a list/vector for 'in' comparison") + + # Loop setup + i1_type = self.getIntegerType(1) + accumulator = cc.AllocaOp(cc.PointerType.get(self.ctx, i1_type), + TypeAttr.get(i1_type)).result + cc.StoreOp(self.getConstantInt(0, 1), accumulator) + + # Element comparison loop + def check_element(idx): + element = self.__load_vector_element(right_val, idx) + promoted_left, promoted_element = self.convertArithmeticToSuperiorType( + [left_val, element], + self.__get_superior_type(left_val, element)) + + iCondPred = IntegerAttr.get(self.getIntegerType(), 0) + fCondPred = IntegerAttr.get(self.getIntegerType(), 0) + cmp_result = ( + arith.CmpIOp(iCondPred, promoted_left, promoted_element) + if IntegerType.isinstance(promoted_left.type) else + arith.CmpFOp(fCondPred, promoted_left, promoted_element)) + + current = cc.LoadOp(accumulator).result + cc.StoreOp(arith.OrIOp(current, cmp_result.result), accumulator) + + self.createInvariantForLoop(self.__get_vector_size(right_val), + check_element) + + final_result = cc.LoadOp(accumulator).result + if isinstance(op, ast.NotIn): + final_result = arith.XOrIOp(final_result, + self.getConstantInt(1, 1)).result + self.pushValue(final_result) + + return + def visit_AugAssign(self, node): """ Visit augment-assign operations (e.g. +=). diff --git a/python/cudaq/kernels/uccsd.py b/python/cudaq/kernels/uccsd.py index d8754226ea..b6de4247a2 100644 --- a/python/cudaq/kernels/uccsd.py +++ b/python/cudaq/kernels/uccsd.py @@ -350,29 +350,37 @@ def uccsd_odd_electrons(qubits: cudaq.qview, thetas: list[float], lenVirtA = len(virtual_alpha_indices) lenVirtB = len(virtual_beta_indices) - singles_a = [[0, 0] for k in range(lenOccA * lenVirtA)] + singles_a0 = [0 for k in range(lenOccA * lenVirtA)] + singles_a1 = [0 for k in range(lenOccA * lenVirtA)] counter = 0 for p in occupied_alpha_indices: for q in virtual_alpha_indices: - singles_a[counter] = [p, q] + singles_a0[counter] = p + singles_a1[counter] = q counter = counter + 1 counter = 0 - singles_b = [[0, 0] for k in range(lenOccB * lenVirtB)] + singles_b0 = [0 for k in range(lenOccB * lenVirtB)] + singles_b1 = [0 for k in range(lenOccB * lenVirtB)] for p in occupied_beta_indices: for q in virtual_beta_indices: - singles_b[counter] = [p, q] + singles_b0[counter] = p + singles_b1[counter] = q counter = counter + 1 counter = 0 - doubles_m = [ - [0, 0, 0, 0] for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA) - ] + doubles_m0 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m1 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m2 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m3 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] for p in occupied_alpha_indices: for q in occupied_beta_indices: for r in virtual_beta_indices: for s in virtual_alpha_indices: - doubles_m[counter] = [p, q, r, s] + doubles_m0[counter] = p + doubles_m1[counter] = q + doubles_m2[counter] = r + doubles_m3[counter] = s counter = counter + 1 counter = 0 @@ -384,13 +392,18 @@ def uccsd_odd_electrons(qubits: cudaq.qview, thetas: list[float], nEle = nEle + 1 counter = 0 - doubles_a = [[0, 0, 0, 0] for k in range(nEle)] + doubles_a0 = [0 for k in range(nEle)] + doubles_a1 = [0 for k in range(nEle)] + doubles_a2 = [0 for k in range(nEle)] + doubles_a3 = [0 for k in range(nEle)] for p in range(lenOccA - 1): for q in range(p + 1, lenOccA): for r in range(lenVirtA - 1): for s in range(r + 1, lenVirtA): - doubles_a[counter] = [occupied_alpha_indices[p],occupied_alpha_indices[q],\ - virtual_alpha_indices[r],virtual_alpha_indices[s]] + doubles_a0[counter] = occupied_alpha_indices[p] + doubles_a1[counter] = occupied_alpha_indices[q] + doubles_a2[counter] = virtual_alpha_indices[r] + doubles_a3[counter] = virtual_alpha_indices[s] counter = counter + 1 counter = 0 @@ -400,47 +413,53 @@ def uccsd_odd_electrons(qubits: cudaq.qview, thetas: list[float], for r in range(lenVirtB - 1): for s in range(r + 1, lenVirtB): nEle = nEle + 1 - doubles_b = [[0, 0, 0, 0] for k in range(nEle)] + + doubles_b0 = [0 for k in range(nEle)] + doubles_b1 = [0 for k in range(nEle)] + doubles_b2 = [0 for k in range(nEle)] + doubles_b3 = [0 for k in range(nEle)] for p in range(lenOccB - 1): for q in range(p + 1, lenOccB): for r in range(lenVirtB - 1): for s in range(r + 1, lenVirtB): - doubles_b[counter] = [occupied_beta_indices[p],occupied_beta_indices[q],\ - virtual_beta_indices[r],virtual_beta_indices[s]] + doubles_b0[counter] = occupied_beta_indices[p] + doubles_b1[counter] = occupied_beta_indices[q] + doubles_b2[counter] = virtual_beta_indices[r] + doubles_b3[counter] = virtual_beta_indices[s] counter = counter + 1 - n_alpha_singles = len(singles_a) - n_beta_singles = len(singles_b) - n_mixed_doubles = len(doubles_m) - n_alpha_doubles = len(doubles_a) - n_beta_doubles = len(doubles_b) + n_alpha_singles = len(singles_a0) + n_beta_singles = len(singles_b0) + n_mixed_doubles = len(doubles_m0) + n_alpha_doubles = len(doubles_a0) + n_beta_doubles = len(doubles_b0) thetaCounter = 0 for i in range(n_alpha_singles): - single_excitation(qubits, singles_a[i][0], singles_a[i][1], + single_excitation(qubits, singles_a0[i], singles_a1[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_beta_singles): - single_excitation(qubits, singles_b[i][0], singles_b[i][1], + single_excitation(qubits, singles_b0[i], singles_b1[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_mixed_doubles): - double_excitation_opt(qubits, doubles_m[i][0], doubles_m[i][1], - doubles_m[i][2], doubles_m[i][3], + double_excitation_opt(qubits, doubles_m0[i], doubles_m1[i], + doubles_m2[i], doubles_m3[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_alpha_doubles): - double_excitation_opt(qubits, doubles_a[i][0], doubles_a[i][1], - doubles_a[i][2], doubles_a[i][3], + double_excitation_opt(qubits, doubles_a0[i], doubles_a1[i], + doubles_a2[i], doubles_a3[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_beta_doubles): - double_excitation_opt(qubits, doubles_b[i][0], doubles_b[i][1], - doubles_b[i][2], doubles_b[i][3], + double_excitation_opt(qubits, doubles_b0[i], doubles_b1[i], + doubles_b2[i], doubles_b3[i], thetas[thetaCounter]) thetaCounter += 1 @@ -463,29 +482,37 @@ def uccsd_even_electrons(qubits: cudaq.qview, thetas: list[float], lenVirtA = len(virtual_alpha_indices) lenVirtB = len(virtual_beta_indices) - singles_a = [[0, 0] for k in range(lenOccA * lenVirtA)] + singles_a0 = [0 for k in range(lenOccA * lenVirtA)] + singles_a1 = [0 for k in range(lenOccA * lenVirtA)] counter = 0 for p in occupied_alpha_indices: for q in virtual_alpha_indices: - singles_a[counter] = [p, q] + singles_a0[counter] = p + singles_a1[counter] = q counter = counter + 1 counter = 0 - singles_b = [[0, 0] for k in range(lenOccB * lenVirtB)] + singles_b0 = [0 for k in range(lenOccB * lenVirtB)] + singles_b1 = [0 for k in range(lenOccB * lenVirtB)] for p in occupied_beta_indices: for q in virtual_beta_indices: - singles_b[counter] = [p, q] + singles_b0[counter] = p + singles_b1[counter] = q counter = counter + 1 counter = 0 - doubles_m = [ - [0, 0, 0, 0] for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA) - ] + doubles_m0 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m1 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m2 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m3 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] for p in occupied_alpha_indices: for q in occupied_beta_indices: for r in virtual_beta_indices: for s in virtual_alpha_indices: - doubles_m[counter] = [p, q, r, s] + doubles_m0[counter] = p + doubles_m1[counter] = q + doubles_m2[counter] = r + doubles_m3[counter] = s counter = counter + 1 counter = 0 @@ -497,13 +524,18 @@ def uccsd_even_electrons(qubits: cudaq.qview, thetas: list[float], nEle = nEle + 1 counter = 0 - doubles_a = [[0, 0, 0, 0] for k in range(nEle)] + doubles_a0 = [0 for k in range(nEle)] + doubles_a1 = [0 for k in range(nEle)] + doubles_a2 = [0 for k in range(nEle)] + doubles_a3 = [0 for k in range(nEle)] for p in range(lenOccA - 1): for q in range(p + 1, lenOccA): for r in range(lenVirtA - 1): for s in range(r + 1, lenVirtA): - doubles_a[counter] = [occupied_alpha_indices[p],occupied_alpha_indices[q],\ - virtual_alpha_indices[r],virtual_alpha_indices[s]] + doubles_a0[counter] = occupied_alpha_indices[p] + doubles_a1[counter] = occupied_alpha_indices[q] + doubles_a2[counter] = virtual_alpha_indices[r] + doubles_a3[counter] = virtual_alpha_indices[s] counter = counter + 1 counter = 0 @@ -513,47 +545,53 @@ def uccsd_even_electrons(qubits: cudaq.qview, thetas: list[float], for r in range(lenVirtB - 1): for s in range(r + 1, lenVirtB): nEle = nEle + 1 - doubles_b = [[0, 0, 0, 0] for k in range(nEle)] + + doubles_b0 = [0 for k in range(nEle)] + doubles_b1 = [0 for k in range(nEle)] + doubles_b2 = [0 for k in range(nEle)] + doubles_b3 = [0 for k in range(nEle)] for p in range(lenOccB - 1): for q in range(p + 1, lenOccB): for r in range(lenVirtB - 1): for s in range(r + 1, lenVirtB): - doubles_b[counter] = [occupied_beta_indices[p],occupied_beta_indices[q],\ - virtual_beta_indices[r],virtual_beta_indices[s]] + doubles_b0[counter] = occupied_beta_indices[p] + doubles_b1[counter] = occupied_beta_indices[q] + doubles_b2[counter] = virtual_beta_indices[r] + doubles_b3[counter] = virtual_beta_indices[s] counter = counter + 1 - n_alpha_singles = len(singles_a) - n_beta_singles = len(singles_b) - n_mixed_doubles = len(doubles_m) - n_alpha_doubles = len(doubles_a) - n_beta_doubles = len(doubles_b) + n_alpha_singles = len(singles_a0) + n_beta_singles = len(singles_b0) + n_mixed_doubles = len(doubles_m0) + n_alpha_doubles = len(doubles_a0) + n_beta_doubles = len(doubles_b0) thetaCounter = 0 for i in range(n_alpha_singles): - single_excitation(qubits, singles_a[i][0], singles_a[i][1], + single_excitation(qubits, singles_a0[i], singles_a1[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_beta_singles): - single_excitation(qubits, singles_b[i][0], singles_b[i][1], + single_excitation(qubits, singles_b0[i], singles_b1[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_mixed_doubles): - double_excitation_opt(qubits, doubles_m[i][0], doubles_m[i][1], - doubles_m[i][2], doubles_m[i][3], + double_excitation_opt(qubits, doubles_m0[i], doubles_m1[i], + doubles_m2[i], doubles_m3[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_alpha_doubles): - double_excitation_opt(qubits, doubles_a[i][0], doubles_a[i][1], - doubles_a[i][2], doubles_a[i][3], + double_excitation_opt(qubits, doubles_a0[i], doubles_a1[i], + doubles_a2[i], doubles_a3[i], thetas[thetaCounter]) thetaCounter += 1 for i in range(n_beta_doubles): - double_excitation_opt(qubits, doubles_b[i][0], doubles_b[i][1], - doubles_b[i][2], doubles_b[i][3], + double_excitation_opt(qubits, doubles_b0[i], doubles_b1[i], + doubles_b2[i], doubles_b3[i], thetas[thetaCounter]) thetaCounter += 1 diff --git a/python/runtime/cudaq/platform/py_alt_launch_kernel.cpp b/python/runtime/cudaq/platform/py_alt_launch_kernel.cpp index 3232e26b11..b9d21ded78 100644 --- a/python/runtime/cudaq/platform/py_alt_launch_kernel.cpp +++ b/python/runtime/cudaq/platform/py_alt_launch_kernel.cpp @@ -106,6 +106,14 @@ jitAndCreateArgs(const std::string &name, MlirModule module, pm.addPass(createSymbolDCEPass()); cudaq::opt::addPipelineConvertToQIR(pm); + auto enablePrintMLIREachPass = + getEnvBool("CUDAQ_MLIR_PRINT_EACH_PASS", false); + + if (enablePrintMLIREachPass) { + cloned.getContext()->disableMultithreading(); + pm.enableIRPrinting(); + } + DefaultTimingManager tm; tm.setEnabled(cudaq::isTimingTagEnabled(cudaq::TIMING_JIT_PASSES)); auto timingScope = tm.getRootScope(); // starts the timer diff --git a/python/tests/backends/test_IQM.py b/python/tests/backends/test_IQM.py index 718d5ae3e9..5747fa1a5c 100644 --- a/python/tests/backends/test_IQM.py +++ b/python/tests/backends/test_IQM.py @@ -65,6 +65,7 @@ def startUpMockServer(): pytest.exit("Mock server did not start in time, skipping tests.", returncode=1) + cudaq.set_random_seed(13) # Set the targeted QPU os.environ["IQM_TOKENS_FILE"] = tmp_tokens_file.name kwargs = {"qpu-architecture": "Apollo"} @@ -227,8 +228,7 @@ def basic_x(): custom_x(qubit) counts = cudaq.sample(basic_x) - counts.dump() - # Gives result like { 1:999 0:0 } + # Gives result like { 0:0 1:1000 } assert counts['0'] == 0 @cudaq.kernel @@ -237,8 +237,8 @@ def basic_h(): custom_h(qubit) counts = cudaq.sample(basic_h) - counts.dump() - assert "0" in counts and "1" in counts + # Gives result like { 0:500 1:500 } + assert counts['0'] > 0 and counts['1'] > 0 @cudaq.kernel def bell(): @@ -247,7 +247,7 @@ def bell(): custom_x.ctrl(qubits[0], qubits[1]) counts = cudaq.sample(bell) - # Gives result like { 11:499 10:0 01:0 00:499 } + # Gives result like { 00:500 01:0 10:0 11:500 } assert counts['01'] == 0 and counts['10'] == 0 @@ -264,7 +264,7 @@ def bell_pair(): custom_cnot(qubits[0], qubits[1]) counts = cudaq.sample(bell_pair) - # Gives result like { 11:499 10:0 01:0 00:499 } + # Gives result like { 00:500 01:0 10:0 11:500 } assert counts['01'] == 0 and counts['10'] == 0 cudaq.register_operation( @@ -281,7 +281,7 @@ def ctrl_z_kernel(): x(controls) counts = cudaq.sample(ctrl_z_kernel) - assert counts["0010011"] == 999 + assert counts["0010011"] == 1000 # leave for gdb debugging diff --git a/python/tests/backends/test_Ionq_LocalEmulation_kernel.py b/python/tests/backends/test_Ionq_LocalEmulation_kernel.py new file mode 100644 index 0000000000..84a7b2bb07 --- /dev/null +++ b/python/tests/backends/test_Ionq_LocalEmulation_kernel.py @@ -0,0 +1,62 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +import cudaq +import cudaq.kernels +import pytest +import os +from typing import List + + +@pytest.fixture(scope="function", autouse=True) +def configureTarget(): + # Set the targeted QPU + cudaq.set_target('ionq', emulate='true') + + yield "Running the tests." + + cudaq.reset_target() + + +def test_Ionq_cudaq_uccsd(): + + num_electrons = 2 + num_qubits = 8 + + thetas = [ + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558 + ] + + @cudaq.kernel + def kernel(): + qubits = cudaq.qvector(num_qubits) + for i in range(num_electrons): + x(qubits[i]) + cudaq.kernels.uccsd(qubits, thetas, num_electrons, num_qubits) + + counts = cudaq.sample(kernel, shots_count=1000) + assert len(counts) == 6 + assert '00000011' in counts + assert '00000110' in counts + assert '00010010' in counts + assert '01000010' in counts + assert '10000001' in counts + assert '11000000' in counts + + +# leave for gdb debugging +if __name__ == "__main__": + loc = os.path.abspath(__file__) + pytest.main([loc, "-s"]) diff --git a/python/tests/backends/test_braket.py b/python/tests/backends/test_braket.py index 504be54910..ec005b52a3 100644 --- a/python/tests/backends/test_braket.py +++ b/python/tests/backends/test_braket.py @@ -26,6 +26,10 @@ def do_something(): cudaq.reset_target() +def assert_close(got) -> bool: + return got < -1.5 and got > -1.9 + + def test_simple_kernel(): @cudaq.kernel @@ -249,8 +253,23 @@ def ansatz(theta: float): hamiltonian = 5.907 - 2.1433 * spin.x(0) * spin.x(1) - 2.1433 * spin.y( 0) * spin.y(1) + .21829 * spin.z(0) - 6.125 * spin.z(1) - res = cudaq.observe(ansatz, hamiltonian, .59, shots_count=1) + res = cudaq.observe(ansatz, hamiltonian, .59, shots_count=2000) print(res.expectation()) + assert assert_close(res.expectation()) + + +def test_observe_async(): + + @cudaq.kernel + def kernel(): + qubits = cudaq.qvector(2) + x(qubits[0]) + + hamiltonian = spin.z(0) * spin.z(1) + future = cudaq.observe_async(kernel, hamiltonian, shots_count=1) + result = future.get() + print(result.expectation()) + assert result.expectation() == -1.0 def test_custom_operations(): diff --git a/python/tests/kernel/test_kernel_features.py b/python/tests/kernel/test_kernel_features.py index c581b741ba..69271c511d 100644 --- a/python/tests/kernel/test_kernel_features.py +++ b/python/tests/kernel/test_kernel_features.py @@ -594,13 +594,13 @@ def kernel3(): @cudaq.kernel def kernel4(): qubits = cudaq.qvector(4) - r = [i * 2 + 1 for i in range(-1)] + r = [i * 2 + 1 for i in range(1)] for i in r: x(qubits[i]) counts = cudaq.sample(kernel4) assert len(counts) == 1 - assert '0000' in counts + assert '0100' in counts @cudaq.kernel def kernel5(): @@ -625,6 +625,25 @@ def kernel6(): assert '0101' in counts +def test_array_value_assignment(): + + @cudaq.kernel() + def foo(): + a = [1, 1] + b = [0, 0] + b[0] = a[0] + b[1] = a[1] + q0 = cudaq.qubit() + q1 = cudaq.qubit() + if (b[0]): + x(q0) + if (b[1]): + x(q1) + + counts = cudaq.sample(foo) + assert "11" in counts + + def test_control_operations(): @cudaq.kernel @@ -1995,6 +2014,41 @@ def invalid_unsupported(): with pytest.raises(RuntimeError): cudaq.sample(invalid_unsupported) +def test_in_comparator(): + + @cudaq.kernel + def kernel(ind: int): + q = cudaq.qubit() + if ind in [6, 13, 20, 27, 34]: + x(q) + + c = cudaq.sample(kernel, 1) + assert len(c) == 1 and '0' in c + c = cudaq.sample(kernel, 20) + assert len(c) == 1 and '1' in c + c = cudaq.sample(kernel, 14) + assert len(c) == 1 and '0' in c + c = cudaq.sample(kernel, 13) + assert len(c) == 1 and '1' in c + c = cudaq.sample(kernel, 26) + assert len(c) == 1 and '0' in c + c = cudaq.sample(kernel, 27) + assert len(c) == 1 and '1' in c + c = cudaq.sample(kernel, 34) + assert len(c) == 1 and '1' in c + c = cudaq.sample(kernel, 36) + assert len(c) == 1 and '0' in c + + @cudaq.kernel + def kernel(ind: int): + q = cudaq.qubit() + if ind not in [6, 13, 20, 27, 34]: + x(q) + + c = cudaq.sample(kernel, 1) + assert len(c) == 1 and '1' in c + c = cudaq.sample(kernel, 20) + assert len(c) == 1 and '0' in c # leave for gdb debugging if __name__ == "__main__": diff --git a/python/tests/kernel/test_kernel_uccsd.py b/python/tests/kernel/test_kernel_uccsd.py new file mode 100644 index 0000000000..4b608a9ecc --- /dev/null +++ b/python/tests/kernel/test_kernel_uccsd.py @@ -0,0 +1,553 @@ +# ============================================================================ # +# Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. # +# All rights reserved. # +# # +# This source code and the accompanying materials are made available under # +# the terms of the Apache License 2.0 which accompanies this distribution. # +# ============================================================================ # + +import pytest +import cudaq + + +# Use a snapshot of the uccsd.py to make sure we can compile +# complex code. Importing uccsd from cudaq.kernels fails due +# clearing the caches in the tests. +# Issue: https://github.com/NVIDIA/cuda-quantum/issues/1954 +def test_cudaq_uccsd1(): + + @cudaq.kernel + def single_excitation1(qubits: cudaq.qview, p_occ: int, q_virt: int, + theta: float): + + # Y_p X_q + rx(np.pi / 2.0, qubits[p_occ]) + h(qubits[q_virt]) + + for i in range(p_occ, q_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(0.5 * theta, qubits[q_virt]) + + for i in range(q_virt, p_occ, -1): + x.ctrl(qubits[i - 1], qubits[i]) + + h(qubits[q_virt]) + rx(-np.pi / 2.0, qubits[p_occ]) + + # -X_p Y_q + h(qubits[p_occ]) + rx(np.pi / 2.0, qubits[q_virt]) + + for i in range(p_occ, q_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(-0.5 * theta, qubits[q_virt]) + + for i in range(q_virt, p_occ, -1): + x.ctrl(qubits[i - 1], qubits[i]) + + rx(-np.pi / 2.0, qubits[q_virt]) + h(qubits[p_occ]) + + @cudaq.kernel + def double_excitation_opt1(qubits: cudaq.qview, p_occ: int, q_occ: int, + r_virt: int, s_virt: int, theta: float): + + i_occ = 0 + j_occ = 0 + a_virt = 0 + b_virt = 0 + if (p_occ < q_occ) and (r_virt < s_virt): + i_occ = p_occ + j_occ = q_occ + a_virt = r_virt + b_virt = s_virt + + elif (p_occ > q_occ) and (r_virt > s_virt): + i_occ = q_occ + j_occ = p_occ + a_virt = s_virt + b_virt = r_virt + + elif (p_occ < q_occ) and (r_virt > s_virt): + i_occ = p_occ + j_occ = q_occ + a_virt = s_virt + b_virt = r_virt + # theta *= -1.0 FIXME + theta *= -1. + + elif (p_occ > q_occ) and (r_virt < s_virt): + i_occ = q_occ + j_occ = p_occ + a_virt = r_virt + b_virt = s_virt + theta *= -1.0 + #Block I: x_i x_j x_a y_b + x_i x_j y_a x_b + x_i y_i y_a y_b - x_i y_j x_a x_b + #Block II: - y_i x_j x_a x_b +y_i x_j y_a y_b - y_i x_j x_a x_b - y_i y_j y_a x_b + + h(qubits[i_occ]) + h(qubits[j_occ]) + h(qubits[a_virt]) + rx(np.pi / 2.0, qubits[b_virt]) + + for i in range(i_occ, j_occ): + x.ctrl(qubits[i], qubits[i + 1]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + + rx(-np.pi / 2.0, qubits[b_virt]) + h(qubits[a_virt]) + + rx(np.pi / 2.0, qubits[a_virt]) + h(qubits[b_virt]) + + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(j_occ, i_occ, -1): + x.ctrl(qubits[i - 1], qubits[i]) + + rx(-np.pi / 2.0, qubits[a_virt]) + h(qubits[j_occ]) + + rx(np.pi / 2.0, qubits[j_occ]) + h(qubits[a_virt]) + + for i in range(i_occ, j_occ): + x.ctrl(qubits[i], qubits[i + 1]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(-0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + + h(qubits[b_virt]) + h(qubits[a_virt]) + + rx(np.pi / 2.0, qubits[a_virt]) + rx(np.pi / 2.0, qubits[b_virt]) + + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(j_occ, i_occ, -1): + x.ctrl(qubits[i - 1], qubits[i]) + + rx(-np.pi / 2.0, qubits[j_occ]) + h(qubits[i_occ]) + + rx(np.pi / 2.0, qubits[i_occ]) + h(qubits[j_occ]) + + for i in range(i_occ, j_occ): + x.ctrl(qubits[i], qubits[i + 1]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + + rx(-np.pi / 2.0, qubits[b_virt]) + rx(-np.pi / 2.0, qubits[a_virt]) + + h(qubits[a_virt]) + h(qubits[b_virt]) + + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(-0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(j_occ, i_occ, -1): + x.ctrl(qubits[i - 1], qubits[i]) + + h(qubits[b_virt]) + h(qubits[j_occ]) + + rx(np.pi / 2.0, qubits[j_occ]) + rx(np.pi / 2.0, qubits[b_virt]) + + for i in range(i_occ, j_occ): + x.ctrl(qubits[i], qubits[i + 1]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(-0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + + rx(-np.pi / 2.0, qubits[b_virt]) + h(qubits[a_virt]) + + rx(np.pi / 2.0, qubits[a_virt]) + h(qubits[b_virt]) + + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(a_virt, b_virt): + x.ctrl(qubits[i], qubits[i + 1]) + + rz(-0.125 * theta, qubits[b_virt]) + + for i in range(b_virt, a_virt, -1): + x.ctrl(qubits[i - 1], qubits[i]) + x.ctrl(qubits[j_occ], qubits[a_virt]) + for i in range(j_occ, i_occ, -1): + x.ctrl(qubits[i - 1], qubits[i]) + + h(qubits[b_virt]) + rx(-np.pi / 2.0, qubits[a_virt]) + rx(-np.pi / 2.0, qubits[j_occ]) + rx(-np.pi / 2.0, qubits[i_occ]) + + @cudaq.kernel + def uccsd1_odd_electrons(qubits: cudaq.qview, thetas: list[float], + n_electrons: int, n_qubits: int): + n_spatial_orbitals = n_qubits // 2 + n_occupied = int(np.ceil(n_electrons / 2)) + n_virtual = n_spatial_orbitals - n_occupied + + occupied_alpha_indices = [i * 2 for i in range(n_occupied)] + virtual_alpha_indices = [ + i * 2 + n_electrons + 1 for i in range(n_virtual) + ] + + occupied_beta_indices = [i * 2 + 1 for i in range(n_occupied - 1)] + virtual_beta_indices = [0 for k in range(n_virtual + 1)] + virtual_beta_indices[0] = 2 * n_occupied - 1 + for i in range(n_virtual): + virtual_beta_indices[i + 1] = i * 2 + 1 + n_electrons + + lenOccA = len(occupied_alpha_indices) + lenOccB = len(occupied_beta_indices) + lenVirtA = len(virtual_alpha_indices) + lenVirtB = len(virtual_beta_indices) + + singles_a0 = [0 for k in range(lenOccA * lenVirtA)] + singles_a1 = [0 for k in range(lenOccA * lenVirtA)] + counter = 0 + for p in occupied_alpha_indices: + for q in virtual_alpha_indices: + singles_a0[counter] = p + singles_a1[counter] = q + counter = counter + 1 + + counter = 0 + singles_b0 = [0 for k in range(lenOccB * lenVirtB)] + singles_b1 = [0 for k in range(lenOccB * lenVirtB)] + for p in occupied_beta_indices: + for q in virtual_beta_indices: + singles_b0[counter] = p + singles_b1[counter] = q + counter = counter + 1 + + counter = 0 + doubles_m0 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m1 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m2 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m3 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + for p in occupied_alpha_indices: + for q in occupied_beta_indices: + for r in virtual_beta_indices: + for s in virtual_alpha_indices: + doubles_m0[counter] = p + doubles_m1[counter] = q + doubles_m2[counter] = r + doubles_m3[counter] = s + counter = counter + 1 + + counter = 0 + nEle = 0 + for p in range(lenOccA - 1): + for q in range(p + 1, lenOccA): + for r in range(lenVirtA - 1): + for s in range(r + 1, lenVirtA): + nEle = nEle + 1 + + counter = 0 + doubles_a0 = [0 for k in range(nEle)] + doubles_a1 = [0 for k in range(nEle)] + doubles_a2 = [0 for k in range(nEle)] + doubles_a3 = [0 for k in range(nEle)] + for p in range(lenOccA - 1): + for q in range(p + 1, lenOccA): + for r in range(lenVirtA - 1): + for s in range(r + 1, lenVirtA): + doubles_a0[counter] = occupied_alpha_indices[p] + doubles_a1[counter] = occupied_alpha_indices[q] + doubles_a2[counter] = virtual_alpha_indices[r] + doubles_a3[counter] = virtual_alpha_indices[s] + counter = counter + 1 + + counter = 0 + nEle = 0 + for p in range(lenOccB - 1): + for q in range(p + 1, lenOccB): + for r in range(lenVirtB - 1): + for s in range(r + 1, lenVirtB): + nEle = nEle + 1 + + doubles_b0 = [0 for k in range(nEle)] + doubles_b1 = [0 for k in range(nEle)] + doubles_b2 = [0 for k in range(nEle)] + doubles_b3 = [0 for k in range(nEle)] + for p in range(lenOccB - 1): + for q in range(p + 1, lenOccB): + for r in range(lenVirtB - 1): + for s in range(r + 1, lenVirtB): + doubles_b0[counter] = occupied_beta_indices[p] + doubles_b1[counter] = occupied_beta_indices[q] + doubles_b2[counter] = virtual_beta_indices[r] + doubles_b3[counter] = virtual_beta_indices[s] + counter = counter + 1 + + n_alpha_singles = len(singles_a0) + n_beta_singles = len(singles_b0) + n_mixed_doubles = len(doubles_m0) + n_alpha_doubles = len(doubles_a0) + n_beta_doubles = len(doubles_b0) + + thetaCounter = 0 + for i in range(n_alpha_singles): + single_excitation1(qubits, singles_a0[i], singles_a1[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_beta_singles): + single_excitation1(qubits, singles_b0[i], singles_b1[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_mixed_doubles): + double_excitation_opt1(qubits, doubles_m0[i], doubles_m1[i], + doubles_m2[i], doubles_m3[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_alpha_doubles): + double_excitation_opt1(qubits, doubles_a0[i], doubles_a1[i], + doubles_a2[i], doubles_a3[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_beta_doubles): + double_excitation_opt1(qubits, doubles_b0[i], doubles_b1[i], + doubles_b2[i], doubles_b3[i], + thetas[thetaCounter]) + thetaCounter += 1 + + @cudaq.kernel + def uccsd1_even_electrons(qubits: cudaq.qview, thetas: list[float], + n_electrons: int, n_qubits: int): + n_spatial_orbitals = n_qubits // 2 + n_occupied = int(np.ceil(n_electrons / 2)) + n_virtual = n_spatial_orbitals - n_occupied + + occupied_alpha_indices = [i * 2 for i in range(n_occupied)] + virtual_alpha_indices = [i * 2 + n_electrons for i in range(n_virtual)] + + occupied_beta_indices = [i * 2 + 1 for i in range(n_occupied)] + virtual_beta_indices = [ + i * 2 + 1 + n_electrons for i in range(n_virtual) + ] + + lenOccA = len(occupied_alpha_indices) + lenOccB = len(occupied_beta_indices) + lenVirtA = len(virtual_alpha_indices) + lenVirtB = len(virtual_beta_indices) + + singles_a0 = [0 for k in range(lenOccA * lenVirtA)] + singles_a1 = [0 for k in range(lenOccA * lenVirtA)] + counter = 0 + for p in occupied_alpha_indices: + for q in virtual_alpha_indices: + singles_a0[counter] = p + singles_a1[counter] = q + counter = counter + 1 + + counter = 0 + singles_b0 = [0 for k in range(lenOccB * lenVirtB)] + singles_b1 = [0 for k in range(lenOccB * lenVirtB)] + for p in occupied_beta_indices: + for q in virtual_beta_indices: + singles_b0[counter] = p + singles_b1[counter] = q + counter = counter + 1 + + counter = 0 + doubles_m0 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m1 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m2 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + doubles_m3 = [0 for k in range(lenOccB * lenVirtB * lenOccA * lenVirtA)] + for p in occupied_alpha_indices: + for q in occupied_beta_indices: + for r in virtual_beta_indices: + for s in virtual_alpha_indices: + doubles_m0[counter] = p + doubles_m1[counter] = q + doubles_m2[counter] = r + doubles_m3[counter] = s + counter = counter + 1 + + counter = 0 + nEle = 0 + for p in range(lenOccA - 1): + for q in range(p + 1, lenOccA): + for r in range(lenVirtA - 1): + for s in range(r + 1, lenVirtA): + nEle = nEle + 1 + + counter = 0 + doubles_a0 = [0 for k in range(nEle)] + doubles_a1 = [0 for k in range(nEle)] + doubles_a2 = [0 for k in range(nEle)] + doubles_a3 = [0 for k in range(nEle)] + for p in range(lenOccA - 1): + for q in range(p + 1, lenOccA): + for r in range(lenVirtA - 1): + for s in range(r + 1, lenVirtA): + doubles_a0[counter] = occupied_alpha_indices[p] + doubles_a1[counter] = occupied_alpha_indices[q] + doubles_a2[counter] = virtual_alpha_indices[r] + doubles_a3[counter] = virtual_alpha_indices[s] + counter = counter + 1 + + counter = 0 + nEle = 0 + for p in range(lenOccB - 1): + for q in range(p + 1, lenOccB): + for r in range(lenVirtB - 1): + for s in range(r + 1, lenVirtB): + nEle = nEle + 1 + + doubles_b0 = [0 for k in range(nEle)] + doubles_b1 = [0 for k in range(nEle)] + doubles_b2 = [0 for k in range(nEle)] + doubles_b3 = [0 for k in range(nEle)] + for p in range(lenOccB - 1): + for q in range(p + 1, lenOccB): + for r in range(lenVirtB - 1): + for s in range(r + 1, lenVirtB): + doubles_b0[counter] = occupied_beta_indices[p] + doubles_b1[counter] = occupied_beta_indices[q] + doubles_b2[counter] = virtual_beta_indices[r] + doubles_b3[counter] = virtual_beta_indices[s] + counter = counter + 1 + + n_alpha_singles = len(singles_a0) + n_beta_singles = len(singles_b0) + n_mixed_doubles = len(doubles_m0) + n_alpha_doubles = len(doubles_a0) + n_beta_doubles = len(doubles_b0) + + thetaCounter = 0 + for i in range(n_alpha_singles): + single_excitation1(qubits, singles_a0[i], singles_a1[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_beta_singles): + single_excitation1(qubits, singles_b0[i], singles_b1[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_mixed_doubles): + double_excitation_opt1(qubits, doubles_m0[i], doubles_m1[i], + doubles_m2[i], doubles_m3[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_alpha_doubles): + double_excitation_opt1(qubits, doubles_a0[i], doubles_a1[i], + doubles_a2[i], doubles_a3[i], + thetas[thetaCounter]) + thetaCounter += 1 + + for i in range(n_beta_doubles): + double_excitation_opt1(qubits, doubles_b0[i], doubles_b1[i], + doubles_b2[i], doubles_b3[i], + thetas[thetaCounter]) + thetaCounter += 1 + + @cudaq.kernel + def uccsd1(qubits: cudaq.qview, thetas: list[float], n_electrons: int, + n_qubits: int): + """ + Generate the unitary coupled cluster singlet doublet CUDA-Q kernel. + + Args: + qubits (:class:`qview`): Pre-allocated qubits + thetas (list[float]): List of parameters + n_electrons (int): Number of electrons + n_qubits (int): Number of qubits + """ + + if n_electrons % 2 == 0: + uccsd1_even_electrons(qubits, thetas, n_electrons, n_qubits) + else: + uccsd1_odd_electrons(qubits, thetas, n_electrons, n_qubits) + + num_electrons = 2 + num_qubits = 8 + + thetas = [ + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558 + ] + + @cudaq.kernel + def kernel(): + qubits = cudaq.qvector(num_qubits) + for i in range(num_electrons): + x(qubits[i]) + uccsd1(qubits, thetas, num_electrons, num_qubits) + + counts = cudaq.sample(kernel, shots_count=1000) + assert len(counts) == 6 + assert '00000011' in counts + assert '00000110' in counts + assert '00010010' in counts + assert '01000010' in counts + assert '10000001' in counts + assert '11000000' in counts diff --git a/runtime/common/BaseRemoteRESTQPU.h b/runtime/common/BaseRemoteRESTQPU.h index bdf74957eb..90a9836102 100644 --- a/runtime/common/BaseRemoteRESTQPU.h +++ b/runtime/common/BaseRemoteRESTQPU.h @@ -293,8 +293,11 @@ class BaseRemoteRESTQPU : public cudaq::QPU { } std::string allowEarlyExitSetting = (codegenTranslation == "qir-adaptive") ? "1" : "0"; - passPipelineConfig = std::string("cc-loop-unroll{allow-early-exit=") + - allowEarlyExitSetting + "}," + passPipelineConfig; + + passPipelineConfig = + std::string( + "func.func(memtoreg{quantum=0},cc-loop-unroll{allow-early-exit=") + + allowEarlyExitSetting + "})," + passPipelineConfig; auto disableQM = backendConfig.find("disable_qubit_mapping"); if (disableQM != backendConfig.end() && disableQM->second == "true") { diff --git a/runtime/cudaq/platform/default/rest/helpers/anyon/anyon.yml b/runtime/cudaq/platform/default/rest/helpers/anyon/anyon.yml index 13451f89e9..00a72c32d8 100644 --- a/runtime/cudaq/platform/default/rest/helpers/anyon/anyon.yml +++ b/runtime/cudaq/platform/default/rest/helpers/anyon/anyon.yml @@ -16,7 +16,7 @@ config: # Add the rest-qpu library to the link list link-libs: ["-lcudaq-rest-qpu"] # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),anyon-%Q_GATE%-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),anyon-%Q_GATE%-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" # Tell the rest-qpu that we are generating Adaptive QIR. codegen-emission: qir-adaptive # Library mode is only for simulators, physical backends must turn this off diff --git a/runtime/cudaq/platform/default/rest/helpers/braket/BraketExecutor.cpp b/runtime/cudaq/platform/default/rest/helpers/braket/BraketExecutor.cpp index 2ea8eea8ba..66b376d283 100644 --- a/runtime/cudaq/platform/default/rest/helpers/braket/BraketExecutor.cpp +++ b/runtime/cudaq/platform/default/rest/helpers/braket/BraketExecutor.cpp @@ -210,7 +210,7 @@ BraketExecutor::execute(std::vector &codesToExecute, return std::async( std::launch::async, - [this, codesToExecute]( + [this, codesToExecute, isObserve]( std::vector createOutcomes) { std::vector results; @@ -271,9 +271,16 @@ BraketExecutor::execute(std::vector &codesToExecute, auto c = serverHelper->processResults(resultsJson, taskArn); - for (auto ®Name : c.register_names()) { - results.emplace_back(c.to_map(regName), regName); - results.back().sequentialData = c.sequential_data(regName); + if (isObserve) { + // Use the job name instead of the global register. + results.emplace_back(c.to_map(), codesToExecute[i].name); + results.back().sequentialData = c.sequential_data(); + } else { + // For each register, add the results into result. + for (auto ®Name : c.register_names()) { + results.emplace_back(c.to_map(regName), regName); + results.back().sequentialData = c.sequential_data(regName); + } } i++; } diff --git a/runtime/cudaq/platform/default/rest/helpers/braket/braket.yml b/runtime/cudaq/platform/default/rest/helpers/braket/braket.yml index 0c0e3784a7..7e2b573f65 100644 --- a/runtime/cudaq/platform/default/rest/helpers/braket/braket.yml +++ b/runtime/cudaq/platform/default/rest/helpers/braket/braket.yml @@ -17,7 +17,7 @@ config: # Tell NVQ++ to generate glue code to set the target backend name gen-target-backend: true # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,unrolling-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX,RxAdjToRx,RyAdjToRy,RzAdjToRz},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,R1ToU3,U3ToRotations,CHToCX,CCZToCX,CRzToCX,CRyToCX,CRxToCX,CR1ToCX,RxAdjToRx,RyAdjToRy,RzAdjToRz},quake-to-cc-prep,func.func(expand-control-veqs,combine-quantum-alloc,canonicalize,combine-measurements),symbol-dce" # Tell the rest-qpu that we are generating OpenQASM 2.0. codegen-emission: qasm2 # Library mode is only for simulators, physical backends must turn this off diff --git a/runtime/cudaq/platform/default/rest/helpers/infleqtion/infleqtion.yml b/runtime/cudaq/platform/default/rest/helpers/infleqtion/infleqtion.yml index 81d151a6f1..a9fa948452 100644 --- a/runtime/cudaq/platform/default/rest/helpers/infleqtion/infleqtion.yml +++ b/runtime/cudaq/platform/default/rest/helpers/infleqtion/infleqtion.yml @@ -17,7 +17,7 @@ config: # Tell NVQ++ to generate glue code to set the target backend name gen-target-backend: true # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,unrolling-pipeline,func.func(lower-to-cfg),canonicalize,func.func(multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,CCZToCX,CRyToCX,CRxToCX,R1AdjToR1,RxAdjToRx,RyAdjToRy,RzAdjToRz},quake-to-cc-prep,func.func(memtoreg{quantum=0}),symbol-dce" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,classical-optimization-pipeline,func.func(lower-to-cfg),canonicalize,func.func(multicontrol-decomposition),decomposition{enable-patterns=SToR1,TToR1,CCZToCX,CRyToCX,CRxToCX,R1AdjToR1,RxAdjToRx,RyAdjToRy,RzAdjToRz},quake-to-cc-prep,func.func(memtoreg{quantum=0}),symbol-dce" # Tell the rest-qpu that we are generating OpenQASM 2.0. codegen-emission: qasm2 # Library mode is only for simulators, physical backends must turn this off diff --git a/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml b/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml index 72ce7f2d65..23e29a15d1 100644 --- a/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml +++ b/runtime/cudaq/platform/default/rest/helpers/ionq/ionq.yml @@ -16,7 +16,7 @@ config: # Add the rest-qpu library to the link list link-libs: ["-lcudaq-rest-qpu"] # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),ionq-gate-set-mapping" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),ionq-gate-set-mapping" # Tell the rest-qpu that we are generating QIR. codegen-emission: qir-base # Additional passes to run after lowering to QIR diff --git a/runtime/cudaq/platform/default/rest/helpers/iqm/iqm.yml b/runtime/cudaq/platform/default/rest/helpers/iqm/iqm.yml index 546327f7bb..67aa5ab451 100644 --- a/runtime/cudaq/platform/default/rest/helpers/iqm/iqm.yml +++ b/runtime/cudaq/platform/default/rest/helpers/iqm/iqm.yml @@ -16,7 +16,7 @@ config: # Add the rest-qpu library to the link list link-libs: ["-lcudaq-rest-qpu"] # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),iqm-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(delay-measurements,regtomem),symbol-dce,iqm-gate-set-mapping" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),iqm-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(delay-measurements,regtomem),symbol-dce,iqm-gate-set-mapping" # Tell the rest-qpu that we are generating IQM JSON. codegen-emission: iqm # Library mode is only for simulators, physical backends must turn this off diff --git a/runtime/cudaq/platform/default/rest/helpers/oqc/oqc.yml b/runtime/cudaq/platform/default/rest/helpers/oqc/oqc.yml index 3ba8809a8a..73bb0aec7a 100644 --- a/runtime/cudaq/platform/default/rest/helpers/oqc/oqc.yml +++ b/runtime/cudaq/platform/default/rest/helpers/oqc/oqc.yml @@ -16,7 +16,7 @@ config: # Add the rest-qpu library to the link list link-libs: ["-lcudaq-rest-qpu"] # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),oqc-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),oqc-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" # Tell the rest-qpu that we are generating QIR. codegen-emission: qir-base # Library mode is only for simulators, physical backends must turn this off diff --git a/runtime/cudaq/platform/default/rest/helpers/quantinuum/quantinuum.yml b/runtime/cudaq/platform/default/rest/helpers/quantinuum/quantinuum.yml index dbacbbfbf8..440a3ab297 100644 --- a/runtime/cudaq/platform/default/rest/helpers/quantinuum/quantinuum.yml +++ b/runtime/cudaq/platform/default/rest/helpers/quantinuum/quantinuum.yml @@ -16,7 +16,7 @@ config: # Add the rest-qpu library to the link list link-libs: ["-lcudaq-rest-qpu"] # Define the lowering pipeline - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),quantinuum-gate-set-mapping" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),quantinuum-gate-set-mapping" # Tell the rest-qpu that we are generating Adaptive QIR. codegen-emission: qir-adaptive # Library mode is only for simulators, physical backends must turn this off diff --git a/runtime/cudaq/platform/fermioniq/fermioniq.yml b/runtime/cudaq/platform/fermioniq/fermioniq.yml index 5f122d71f3..ec87efd03f 100644 --- a/runtime/cudaq/platform/fermioniq/fermioniq.yml +++ b/runtime/cudaq/platform/fermioniq/fermioniq.yml @@ -18,7 +18,7 @@ config: # Library mode is only for simulators, physical backends must turn this off library-mode: false # lowering config - platform-lowering-config: "func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),fermioniq-gate-set-mapping" + platform-lowering-config: "classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),fermioniq-gate-set-mapping" # Tell the rest-qpu that we are generating QIR. codegen-emission: qir-base diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 51eba42425..8d6fb34450 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -23,6 +23,7 @@ find_file(CUDENSITYMAT_INC ) message(STATUS "cudensitymat header: ${CUDENSITYMAT_INC}") +get_filename_component(CUDENSITYMAT_INCLUDE_DIR ${CUDENSITYMAT_INC} DIRECTORY) add_library(${LIBRARY_NAME} SHARED CuDensityMatSim.cpp diff --git a/scripts/validate_container.sh b/scripts/validate_container.sh index 58efa1e1fa..ced784dc3d 100644 --- a/scripts/validate_container.sh +++ b/scripts/validate_container.sh @@ -355,6 +355,30 @@ else echo ":white_flag: Notebooks validation skipped." >> "${tmpFile}" fi +# Python snippet validation +if [ -d "snippets/" ]; +then + # Skip NVQC and multi-GPU snippets. + for ex in `find snippets/ -name '*.py' -not -path '*/nvqc/*' -not -path '*/multi_gpu_workflows/*' | sort`; + do + filename=$(basename -- "$ex") + filename="${filename%.*}" + echo "Testing $filename:" + echo "Source: $ex" + let "samples+=1" + python3 $ex 1> /dev/null + status=$? + echo "Exited with code $status" + if [ "$status" -eq "0" ]; then + let "passed+=1" + echo ":white_check_mark: Successfully ran $filename." >> "${tmpFile}" + else + let "failed+=1" + echo ":x: Failed to run $filename." >> "${tmpFile}" + fi + done +fi + if [ -f "$GITHUB_STEP_SUMMARY" ]; then for t in $requested_backends diff --git a/targettests/TargetConfig/RegressionValidation/anyon.config b/targettests/TargetConfig/RegressionValidation/anyon.config index 954bf0293b..506d7f7061 100644 --- a/targettests/TargetConfig/RegressionValidation/anyon.config +++ b/targettests/TargetConfig/RegressionValidation/anyon.config @@ -20,7 +20,7 @@ # Define the lowering pipeline. telegraph-8q has an 8-qubit ring topology, so mapping # uses ring(8). # Berkeley-25q uses a bidiratctional connectivity lattice with 8 connectivity per qubit in the bulk. -# CHECK-DAG: PLATFORM_LOWERING_CONFIG="func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),anyon-%Q_GATE%-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" +# CHECK-DAG: PLATFORM_LOWERING_CONFIG="classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),anyon-%Q_GATE%-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" # Tell the rest-qpu that we are generating QIR. diff --git a/targettests/TargetConfig/RegressionValidation/ionq.config b/targettests/TargetConfig/RegressionValidation/ionq.config index 337987c3a7..338686eafb 100644 --- a/targettests/TargetConfig/RegressionValidation/ionq.config +++ b/targettests/TargetConfig/RegressionValidation/ionq.config @@ -18,7 +18,7 @@ # CHECK-DAG: LINKLIBS="${LINKLIBS} -lcudaq-rest-qpu" # Define the lowering pipeline -# CHECK-DAG: PLATFORM_LOWERING_CONFIG="func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),ionq-gate-set-mapping" +# CHECK-DAG: PLATFORM_LOWERING_CONFIG="classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),ionq-gate-set-mapping" # Tell the rest-qpu that we are generating QIR. # CHECK-DAG: CODEGEN_EMISSION=qir-base diff --git a/targettests/TargetConfig/RegressionValidation/iqm.config b/targettests/TargetConfig/RegressionValidation/iqm.config index f1ced5aa75..5bb80da7fb 100644 --- a/targettests/TargetConfig/RegressionValidation/iqm.config +++ b/targettests/TargetConfig/RegressionValidation/iqm.config @@ -20,7 +20,7 @@ # Define the lowering pipeline, here we lower to Base QIR # Note: the runtime will dynamically substitute %QPU_ARCH% based on # qpu-architecture -# CHECK-DAG: PLATFORM_LOWERING_CONFIG="func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),iqm-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(delay-measurements,regtomem),symbol-dce,iqm-gate-set-mapping" +# CHECK-DAG: PLATFORM_LOWERING_CONFIG="classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),iqm-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(delay-measurements,regtomem),symbol-dce,iqm-gate-set-mapping" # Tell the rest-qpu that we are generating IQM JSON. # CHECK-DAG: CODEGEN_EMISSION=iqm diff --git a/targettests/TargetConfig/RegressionValidation/oqc.config b/targettests/TargetConfig/RegressionValidation/oqc.config index 4bc0123ef7..072d58d59f 100644 --- a/targettests/TargetConfig/RegressionValidation/oqc.config +++ b/targettests/TargetConfig/RegressionValidation/oqc.config @@ -20,7 +20,7 @@ # Define the lowering pipeline. Lucy has an 8-qubit ring topology, so mapping # uses ring(8). # Toshiko uses a Kagome lattice with 2-3 connectivity per qubit -# CHECK-DAG: PLATFORM_LOWERING_CONFIG="func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),oqc-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" +# CHECK-DAG: PLATFORM_LOWERING_CONFIG="classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),oqc-gate-set-mapping,func.func(add-dealloc,combine-quantum-alloc,canonicalize,factor-quantum-alloc,memtoreg),add-wireset,func.func(assign-wire-indices),qubit-mapping{device=file(%QPU_ARCH%)},func.func(regtomem),symbol-dce" # Tell the rest-qpu that we are generating QIR. diff --git a/targettests/TargetConfig/RegressionValidation/quantinuum.config b/targettests/TargetConfig/RegressionValidation/quantinuum.config index 83d66f5e4e..86bc7860de 100644 --- a/targettests/TargetConfig/RegressionValidation/quantinuum.config +++ b/targettests/TargetConfig/RegressionValidation/quantinuum.config @@ -18,7 +18,7 @@ # CHECK-DAG: LINKLIBS="${LINKLIBS} -lcudaq-rest-qpu" # Define the lowering pipeline, here we lower to Adaptive QIR -# CHECK-DAG: PLATFORM_LOWERING_CONFIG="func.func(const-prop-complex,canonicalize,cse,lift-array-alloc),globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,unrolling-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),quantinuum-gate-set-mapping" +# CHECK-DAG: PLATFORM_LOWERING_CONFIG="classical-optimization-pipeline,globalize-array-values,func.func(state-prep),unitary-synthesis,canonicalize,apply-op-specialization,aggressive-early-inlining,expand-measurements,classical-optimization-pipeline,decomposition{enable-patterns=U3ToRotations},func.func(lower-to-cfg,canonicalize,multicontrol-decomposition),quantinuum-gate-set-mapping" # Tell the rest-qpu that we are generating QIR. # CHECK-DAG: CODEGEN_EMISSION=qir-adaptive diff --git a/targettests/execution/uccsd.cpp b/targettests/execution/uccsd.cpp new file mode 100644 index 0000000000..e692d1c1b7 --- /dev/null +++ b/targettests/execution/uccsd.cpp @@ -0,0 +1,535 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// clang-format off +// RUN: nvq++ %cpp_std --target anyon --emulate %s -o %t && %t | FileCheck %s +// RUN: if %braket_avail; then nvq++ %cpp_std --target braket --emulate %s -o %t && %t | FileCheck %s ; fi +// RUN: nvq++ %cpp_std --target ionq --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target iqm --iqm-machine Apollo --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target oqc --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --target quantinuum --emulate %s -o %t && %t | FileCheck %s +// RUN: nvq++ %cpp_std --enable-mlir %s -o %t +// clang-format on + +#include +#include + +namespace test_uccsd { + +__qpu__ void single_excitation(cudaq::qview<> qubits, double theta, + std::size_t p_occ, std::size_t q_virt) { + // Y_p X_q + rx(M_PI_2, qubits[p_occ]); + h(qubits[q_virt]); + + for (std::size_t i = p_occ; i < q_virt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(0.5 * theta, qubits[q_virt]); + + for (std::size_t i = q_virt; i > p_occ; i--) + cx(qubits[i - 1], qubits[i]); + + h(qubits[q_virt]); + rx(-M_PI_2, qubits[p_occ]); + + // -X_p Y_q + h(qubits[p_occ]); + rx(M_PI_2, qubits[q_virt]); + + for (std::size_t i = p_occ; i < q_virt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(-0.5 * theta, qubits[q_virt]); + + for (std::size_t i = q_virt; i > p_occ; i--) + cx(qubits[i - 1], qubits[i]); + + rx(-M_PI_2, qubits[q_virt]); + h(qubits[p_occ]); +} + +__qpu__ void double_excitation(cudaq::qview<> qubits, double theta, + std::size_t pOcc, std::size_t qOcc, + std::size_t rVirt, std::size_t sVirt) { + std::size_t iOcc = 0, jOcc = 0, aVirt = 0, bVirt = 0; + if ((pOcc < qOcc) && (rVirt < sVirt)) { + iOcc = pOcc; + jOcc = qOcc; + aVirt = rVirt; + bVirt = sVirt; + } else if ((pOcc > qOcc) && (rVirt > sVirt)) { + iOcc = qOcc; + jOcc = pOcc; + aVirt = sVirt; + bVirt = rVirt; + } else if ((pOcc < qOcc) && (rVirt > sVirt)) { + iOcc = pOcc; + jOcc = qOcc; + aVirt = sVirt; + bVirt = rVirt; + theta *= -1.; + } else if ((pOcc > qOcc) && (rVirt < sVirt)) { + iOcc = qOcc; + jOcc = pOcc; + aVirt = rVirt; + bVirt = sVirt; + theta *= -1.; + } + + h(qubits[iOcc]); + h(qubits[jOcc]); + h(qubits[aVirt]); + rx(M_PI_2, qubits[bVirt]); + + for (std::size_t i = iOcc; i < jOcc; i++) + cx(qubits[i], qubits[i + 1]); + + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + + cx(qubits[jOcc], qubits[aVirt]); + + rx(-M_PI_2, qubits[bVirt]); + h(qubits[aVirt]); + + rx(M_PI_2, qubits[aVirt]); + h(qubits[bVirt]); + + cx(qubits[jOcc], qubits[aVirt]); + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = jOcc; i > iOcc; i--) + cx(qubits[i - 1], qubits[i]); + + rx(-M_PI_2, qubits[aVirt]); + h(qubits[jOcc]); + + rx(M_PI_2, qubits[jOcc]); + h(qubits[aVirt]); + + for (std::size_t i = iOcc; i < jOcc; i++) + cx(qubits[i], qubits[i + 1]); + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(-0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + cx(qubits[jOcc], qubits[aVirt]); + + h(qubits[bVirt]); + h(qubits[aVirt]); + + rx(M_PI_2, qubits[aVirt]); + rx(M_PI_2, qubits[bVirt]); + + cx(qubits[jOcc], qubits[aVirt]); + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = jOcc; i > iOcc; i--) + cx(qubits[i - 1], qubits[i]); + + rx(-M_PI_2, qubits[jOcc]); + h(qubits[iOcc]); + + rx(M_PI_2, qubits[iOcc]); + h(qubits[jOcc]); + + for (std::size_t i = iOcc; i < jOcc; i++) + cx(qubits[i], qubits[i + 1]); + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + cx(qubits[jOcc], qubits[aVirt]); + + rx(-M_PI_2, qubits[bVirt]); + rx(-M_PI_2, qubits[aVirt]); + + h(qubits[aVirt]); + h(qubits[bVirt]); + + cx(qubits[jOcc], qubits[aVirt]); + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(-0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = jOcc; i > iOcc; i--) + cx(qubits[i - 1], qubits[i]); + + h(qubits[bVirt]); + h(qubits[jOcc]); + + rx(M_PI_2, qubits[jOcc]); + rx(M_PI_2, qubits[bVirt]); + + for (std::size_t i = iOcc; i < jOcc; i++) + cx(qubits[i], qubits[i + 1]); + + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(-0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + cx(qubits[jOcc], qubits[aVirt]); + + rx(-M_PI_2, qubits[bVirt]); + h(qubits[aVirt]); + + rx(M_PI_2, qubits[aVirt]); + h(qubits[bVirt]); + + cx(qubits[jOcc], qubits[aVirt]); + for (std::size_t i = aVirt; i < bVirt; i++) + cx(qubits[i], qubits[i + 1]); + + rz(-0.125 * theta, qubits[bVirt]); + + for (std::size_t i = bVirt; i > aVirt; i--) + cx(qubits[i - 1], qubits[i]); + cx(qubits[jOcc], qubits[aVirt]); + + for (std::size_t i = jOcc; i > iOcc; i--) + cx(qubits[i - 1], qubits[i]); + + h(qubits[bVirt]); + rx(-M_PI_2, qubits[aVirt]); + rx(-M_PI_2, qubits[jOcc]); + rx(-M_PI_2, qubits[iOcc]); +} + +__qpu__ float positive_floor(float x) { + int integer_part = (int)x; + return (float)integer_part; +} + +__qpu__ std::size_t getNumOccupiedAlpha(std::size_t numElectrons, + std::size_t spin, + std::size_t numQubits) { + auto numSpatialOrbs = numQubits / 2; + if (spin > 0) { + auto n_occupied_beta = static_cast( + positive_floor((float)(numElectrons - spin) / 2)); + auto n_occupied_alpha = numElectrons - n_occupied_beta; + return n_occupied_alpha; + } + + auto n_occupied_alpha = static_cast( + positive_floor((float)numElectrons / 2)); + return n_occupied_alpha; +} + +__qpu__ std::size_t getNumOccupiedBeta(std::size_t numElectrons, + std::size_t spin, + std::size_t numQubits) { + + auto numSpatialOrbs = numQubits / 2; + if (spin > 0) { + auto n_occupied_beta = static_cast( + positive_floor((float)(numElectrons - spin) / 2)); + return n_occupied_beta; + } + + auto n_occupied_alpha = static_cast( + positive_floor((float)numElectrons / 2)); + return n_occupied_alpha; +} + +__qpu__ std::size_t getNumVirtualAlpha(std::size_t numElectrons, + std::size_t spin, + std::size_t numQubits) { + + auto numSpatialOrbs = numQubits / 2; + if (spin > 0) { + auto n_occupied_beta = static_cast( + positive_floor((float)(numElectrons - spin) / 2)); + auto n_occupied_alpha = numElectrons - n_occupied_beta; + auto n_virtual_alpha = numSpatialOrbs - n_occupied_alpha; + return n_virtual_alpha; + } + auto n_occupied_alpha = static_cast( + positive_floor((float)numElectrons / 2)); + auto n_virtual_alpha = numSpatialOrbs - n_occupied_alpha; + return n_virtual_alpha; +} + +__qpu__ std::size_t getNumVirtualBeta(std::size_t numElectrons, + std::size_t spin, std::size_t numQubits) { + + auto numSpatialOrbs = numQubits / 2; + if (spin > 0) { + auto n_occupied_beta = static_cast( + positive_floor((float)(numElectrons - spin) / 2)); + auto n_virtual_beta = numSpatialOrbs - n_occupied_beta; + return n_virtual_beta; + } + + auto n_occupied_alpha = + static_cast(positive_floor((float)numElectrons / 2)); + auto n_virtual_beta = numSpatialOrbs - n_occupied_alpha; + return n_virtual_beta; +} + +__qpu__ void uccsd2(cudaq::qview<> qubits, const std::vector &thetas, + std::size_t numElectrons, std::size_t spin) { + + int numOccAlpha = + getNumOccupiedAlpha(numElectrons, spin, qubits.size()); + int numOccBeta = getNumOccupiedBeta(numElectrons, spin, qubits.size()); + int numVirtAlpha = + getNumVirtualAlpha(numElectrons, spin, qubits.size()); + int numVirtBeta = getNumVirtualBeta(numElectrons, spin, qubits.size()); + std::vector occupiedAlpha(numOccAlpha), + virtualAlpha(numVirtAlpha), occupiedBeta(numOccBeta), + virtualBeta(numVirtBeta); + if (spin > 0) { + + int counter = 0; + for (std::size_t i = 0; i < numOccAlpha; i++) { + occupiedAlpha[counter] = i * 2; + counter++; + } + counter = 0; + + for (std::size_t i = 0; i < numVirtAlpha; i++) { + virtualAlpha[counter] = i * 2 + numElectrons + 1; + counter++; + } + + counter = 0; + for (std::size_t i = 0; i < numOccBeta; i++) { + occupiedBeta[counter] = i * 2 + 1; + counter++; + } + counter = 0; + + for (std::size_t i = 0; i < numVirtBeta; i++) { + virtualBeta[counter] = i * 2 + numElectrons - 1; + counter++; + } + + } else { + auto numOccupied = numOccAlpha; + auto numVirtual = numVirtAlpha; + + int counter = 0; + for (std::size_t i = 0; i < numOccupied; i++) { + occupiedAlpha[counter] = i * 2; + counter++; + } + counter = 0; + for (std::size_t i = 0; i < numVirtual; i++) { + virtualAlpha[counter] = i * 2 + numElectrons; + counter++; + } + counter = 0; + + for (std::size_t i = 0; i < numOccupied; i++) { + occupiedBeta[counter] = i * 2 + 1; + counter++; + } + counter = 0; + for (std::size_t i = 0; i < numVirtual; i++) { + virtualBeta[counter] = i * 2 + numElectrons + 1; + counter++; + } + } + + std::size_t counter = 0; + std::vector singlesAlpha(2 * occupiedAlpha.size() * + virtualAlpha.size()); + for (auto p : occupiedAlpha) + for (auto q : virtualAlpha) { + singlesAlpha[counter] = p; + counter++; + singlesAlpha[counter] = q; + counter++; + } + + counter = 0; + std::vector singlesBeta(2 * occupiedBeta.size() * + virtualBeta.size()); + for (auto p : occupiedBeta) + for (auto q : virtualBeta) { + singlesBeta[counter] = p; + counter++; + singlesBeta[counter] = q; + counter++; + } + + counter = 0; + std::vector doublesMixed( + 4 * occupiedAlpha.size() * virtualAlpha.size() * occupiedBeta.size() * + virtualBeta.size()); + for (auto p : occupiedAlpha) + for (auto q : occupiedBeta) + for (auto r : virtualBeta) + for (auto s : virtualAlpha) { + doublesMixed[counter] = p; + counter++; + doublesMixed[counter] = q; + counter++; + doublesMixed[counter] = r; + counter++; + doublesMixed[counter] = s; + counter++; + } + + counter = 0; + for (int p = 0; p < numOccAlpha - 1; p++) + for (int q = p + 1; q < numOccAlpha; q++) + for (int r = 0; r < numVirtAlpha - 1; r++) + for (int s = r + 1; s < numVirtAlpha; s++) + counter++; + + std::vector doublesAlpha(4 * counter); + counter = 0; + for (int p = 0; p < numOccAlpha - 1; p++) + for (int q = p + 1; q < numOccAlpha; q++) + for (int r = 0; r < numVirtAlpha - 1; r++) + for (int s = r + 1; s < numVirtAlpha; s++) { + doublesAlpha[counter] = occupiedAlpha[p]; + counter++; + doublesAlpha[counter] = occupiedAlpha[q]; + counter++; + doublesAlpha[counter] = virtualAlpha[r]; + counter++; + doublesAlpha[counter] = virtualAlpha[s]; + counter++; + } + + counter = 0; + for (int p = 0; p < numOccBeta - 1; p++) + for (int q = p + 1; q < numOccBeta; q++) + for (int r = 0; r < numVirtBeta - 1; r++) + for (int s = r + 1; s < numVirtBeta; s++) + counter++; + std::vector doublesBeta(4 * counter); + counter = 0; + for (int p = 0; p < numOccBeta - 1; p++) + for (int q = p + 1; q < numOccBeta; q++) + for (int r = 0; r < numVirtBeta - 1; r++) + for (int s = r + 1; s < numVirtBeta; s++) { + doublesBeta[counter] = occupiedBeta[p]; + counter++; + doublesBeta[counter] = occupiedBeta[q]; + counter++; + doublesBeta[counter] = virtualBeta[r]; + counter++; + doublesBeta[counter] = virtualBeta[s]; + counter++; + } + + std::size_t thetaCounter = 0; + for (std::size_t i = 0; i < singlesAlpha.size(); i += 2) + single_excitation(qubits, thetas[thetaCounter++], singlesAlpha[i], + singlesAlpha[i + 1]); + + for (std::size_t i = 0; i < singlesBeta.size(); i += 2) + single_excitation(qubits, thetas[thetaCounter++], singlesBeta[i], + singlesBeta[i + 1]); + + for (std::size_t i = 0; i < doublesMixed.size(); i += 4) + double_excitation(qubits, thetas[thetaCounter++], doublesMixed[i], + doublesMixed[i + 1], doublesMixed[i + 2], + doublesMixed[i + 3]); + + for (std::size_t i = 0; i < doublesAlpha.size(); i += 4) + double_excitation(qubits, thetas[thetaCounter++], doublesAlpha[i], + doublesAlpha[i + 1], doublesAlpha[i + 2], + doublesAlpha[i + 3]); + + for (std::size_t i = 0; i < doublesBeta.size(); i += 4) + double_excitation(qubits, thetas[thetaCounter++], doublesBeta[i], + doublesBeta[i + 1], doublesBeta[i + 2], + doublesBeta[i + 3]); +} + +} // namespace test_uccsd + +__qpu__ void test_trial_state(cudaq::qview<> qubits, std::size_t num_electrons, + const std::vector &thetas) { + for (std::size_t i = 0; i < num_electrons; i++) + x(qubits[i]); + test_uccsd::uccsd2(qubits, thetas, num_electrons, 0); +} + +__qpu__ void test(std::size_t num_qubits, std::size_t num_electrons, + const std::vector &thetas) { + cudaq::qvector qubits(num_qubits); + test_trial_state(qubits, num_electrons, thetas); +} + +void printCounts(cudaq::sample_result &result) { + std::vector values{}; + for (auto &&[bits, counts] : result) { + values.push_back(bits); + } + + std::sort(values.begin(), values.end()); + for (auto &&bits : values) { + std::cout << bits << '\n'; + } +} + +int main() { + std::size_t num_electrons = 2; + std::size_t num_qubits = 6; + std::vector thetas = { + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151, 0.2286823796532558, + -0.00037043841404585794, 0.0003811110195084151}; + + auto counts = cudaq::sample(test, 6, 2, thetas); + printCounts(counts); + return 0; +} + +// CHECK: 000110 +// CHECK: 100100 +// CHECK: 110000 diff --git a/test/AST-Quake/infinite_loop.cpp b/test/AST-Quake/infinite_loop.cpp new file mode 100644 index 0000000000..10c803a78d --- /dev/null +++ b/test/AST-Quake/infinite_loop.cpp @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +// RUN: cudaq-quake %cpp_std %s | cudaq-opt --memtoreg=quantum=0 --canonicalize --cc-loop-normalize |& FileCheck %s + +#include + +// Counted loop structure when condition is always true + +__qpu__ int t1() { + cudaq::qubit q; + for (std::uint32_t u = 1; u <= 0xffffffff; u++) + x(q); + return 0; +} + +__qpu__ int t2() { + cudaq::qubit q; + for (std::int32_t u = 1; u <= 0x7fffffff; u++) + x(q); + return 0; +} + +__qpu__ int t3() { + cudaq::qubit q; + for (std::uint64_t u = 5; u <= 0xffffffffffffffff; u++) + x(q); + return 0; +} + +__qpu__ int t4() { + cudaq::qubit q; + for (std::int64_t u = 16; u <= 0x7fffffffffffffff; u++) + x(q); + return 0; +} + +__qpu__ int t5() { + cudaq::qubit q; + for (std::uint64_t u = -14; u >= 0; u--) + x(q); + return 0; +} + +__qpu__ int t6() { + cudaq::qubit q; + std::int64_t cmp = 0x8000000000000000; + for (std::int64_t u = 83; u >= cmp; u++) + x(q); + return 0; +} + +// CHECK: Loop condition is always true. This loop is not supported in a kernel. +// CHECK: Loop condition is always true. This loop is not supported in a kernel. +// CHECK: Loop condition is always true. This loop is not supported in a kernel. +// CHECK: Loop condition is always true. This loop is not supported in a kernel. +// CHECK: Loop condition is always true. This loop is not supported in a kernel. +// CHECK: Loop condition is always true. This loop is not supported in a kernel. diff --git a/test/AST-Quake/loop_normal.cpp b/test/AST-Quake/loop_normal.cpp index a4bf4bfad5..62ff0aa75b 100644 --- a/test/AST-Quake/loop_normal.cpp +++ b/test/AST-Quake/loop_normal.cpp @@ -402,3 +402,278 @@ __qpu__ void linear_expr6() { // CHECK: %[[VAL_15:.*]] = arith.addi %[[VAL_14]], %[[VAL_1]] : i32 // CHECK: cc.continue %[[VAL_15]] : i32 // CHECK: } {normalized} + +// In cases where the number of iterations is invalid, we should normalize to +// a count of 0. + +__qpu__ void non_iterating_loop2() { + cudaq::qvector q(100); + for (std::int64_t i = 1; i < -1; i++) + x(q[i]); +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_non_iterating_loop2._Z19non_iterating_loop2v() attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK-DAG: %[[VAL_0:.*]] = arith.constant 0 : i64 +// CHECK-DAG: %[[VAL_1:.*]] = arith.constant 1 : i64 +// CHECK-DAG: %[[VAL_2:.*]] = quake.alloca !quake.veq<100> +// CHECK: %[[VAL_3:.*]] = cc.loop while ((%[[VAL_4:.*]] = %[[VAL_0]]) -> (i64)) { +// CHECK: %[[VAL_5:.*]] = arith.cmpi ne, %[[VAL_4]], %[[VAL_0]] : i64 +// CHECK: cc.condition %[[VAL_5]](%[[VAL_4]] : i64) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_6:.*]]: i64): +// CHECK: %[[VAL_7:.*]] = arith.addi %[[VAL_6]], %[[VAL_1]] : i64 +// CHECK: %[[VAL_8:.*]] = quake.extract_ref %[[VAL_2]]{{\[}}%[[VAL_7]]] : (!quake.veq<100>, i64) -> !quake.ref +// CHECK: quake.x %[[VAL_8]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_6]] : i64 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_9:.*]]: i64): +// CHECK: %[[VAL_10:.*]] = arith.addi %[[VAL_9]], %[[VAL_1]] : i64 +// CHECK: cc.continue %[[VAL_10]] : i64 +// CHECK: } {normalized} +// CHECK: return +// CHECK: } + +__qpu__ int f2a() { + cudaq::qubit q; + for (int u = 1; u < 0; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_f2a._Z3f2av() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK-DAG: %[[VAL_0:.*]] = arith.constant 0 : i32 +// CHECK-DAG: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK-DAG: %[[VAL_2:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_3:.*]] = cc.loop while ((%[[VAL_4:.*]] = %[[VAL_0]]) -> (i32)) { +// CHECK: %[[VAL_5:.*]] = arith.cmpi ne, %[[VAL_4]], %[[VAL_0]] : i32 +// CHECK: cc.condition %[[VAL_5]](%[[VAL_4]] : i32) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_6:.*]]: i32): +// CHECK: quake.x %[[VAL_2]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_6]] : i32 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_7:.*]]: i32): +// CHECK: %[[VAL_8:.*]] = arith.addi %[[VAL_7]], %[[VAL_1]] : i32 +// CHECK: cc.continue %[[VAL_8]] : i32 +// CHECK: } {normalized} +// CHECK: return %[[VAL_0]] : i32 +// CHECK: } + +__qpu__ int f2b() { + cudaq::qubit q; + for (int u = 10; u < 0; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_f2b._Z3f2bv() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK-DAG: %[[VAL_0:.*]] = arith.constant 0 : i32 +// CHECK-DAG: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK-DAG: %[[VAL_2:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_3:.*]] = cc.loop while ((%[[VAL_4:.*]] = %[[VAL_0]]) -> (i32)) { +// CHECK: %[[VAL_5:.*]] = arith.cmpi ne, %[[VAL_4]], %[[VAL_0]] : i32 +// CHECK: cc.condition %[[VAL_5]](%[[VAL_4]] : i32) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_6:.*]]: i32): +// CHECK: quake.x %[[VAL_2]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_6]] : i32 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_7:.*]]: i32): +// CHECK: %[[VAL_8:.*]] = arith.addi %[[VAL_7]], %[[VAL_1]] : i32 +// CHECK: cc.continue %[[VAL_8]] : i32 +// CHECK: } {normalized} +// CHECK: return %[[VAL_0]] : i32 +// CHECK: } + +__qpu__ int f4() { + cudaq::qubit q; + for (std::int64_t u = 6; u < 0; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_f4._Z2f4v() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK-DAG: %[[VAL_0:.*]] = arith.constant 0 : i64 +// CHECK-DAG: %[[VAL_1:.*]] = arith.constant 1 : i64 +// CHECK-DAG: %[[VAL_2:.*]] = arith.constant 0 : i32 +// CHECK-DAG: %[[VAL_3:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_4:.*]] = cc.loop while ((%[[VAL_5:.*]] = %[[VAL_0]]) -> (i64)) { +// CHECK: %[[VAL_6:.*]] = arith.cmpi ne, %[[VAL_5]], %[[VAL_0]] : i64 +// CHECK: cc.condition %[[VAL_6]](%[[VAL_5]] : i64) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_7:.*]]: i64): +// CHECK: quake.x %[[VAL_3]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_7]] : i64 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_8:.*]]: i64): +// CHECK: %[[VAL_9:.*]] = arith.addi %[[VAL_8]], %[[VAL_1]] : i64 +// CHECK: cc.continue %[[VAL_9]] : i64 +// CHECK: } {normalized} +// CHECK: return %[[VAL_2]] : i32 +// CHECK: } + +__qpu__ int m1(unsigned z) { + cudaq::qubit q; + for (unsigned u = 1; u < z; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_m1._Z2m1j( +// CHECK-SAME: %[[VAL_0:.*]]: i32) -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK-DAG: %[[VAL_1:.*]] = arith.constant 0 : i32 +// CHECK-DAG: %[[VAL_2:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_3:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_4:.*]] = arith.subi %[[VAL_0]], %[[VAL_2]] : i32 +// CHECK: %[[VAL_7:.*]] = cc.loop while ((%[[VAL_8:.*]] = %[[VAL_1]]) -> (i32)) { +// CHECK: %[[VAL_9:.*]] = arith.cmpi ne, %[[VAL_8]], %[[VAL_4]] : i32 +// CHECK: cc.condition %[[VAL_9]](%[[VAL_8]] : i32) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_10:.*]]: i32): +// CHECK: quake.x %[[VAL_3]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_10]] : i32 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_11:.*]]: i32): +// CHECK: %[[VAL_12:.*]] = arith.addi %[[VAL_11]], %[[VAL_2]] : i32 +// CHECK: cc.continue %[[VAL_12]] : i32 +// CHECK: } {normalized} +// CHECK: return %[[VAL_1]] : i32 +// CHECK: } + +__qpu__ int m2(int z) { + cudaq::qubit q; + for (int u = 1; u < z; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_m2._Z2m2i( +// CHECK-SAME: %[[VAL_0:.*]]: i32) -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK-DAG: %[[VAL_1:.*]] = arith.constant 0 : i32 +// CHECK-DAG: %[[VAL_2:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_3:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_4:.*]] = arith.subi %[[VAL_0]], %[[VAL_2]] : i32 +// CHECK: %[[VAL_5:.*]] = arith.cmpi sgt, %[[VAL_4]], %[[VAL_1]] : i32 +// CHECK: %[[VAL_6:.*]] = arith.select %[[VAL_5]], %[[VAL_4]], %[[VAL_1]] : i32 +// CHECK: %[[VAL_7:.*]] = cc.loop while ((%[[VAL_8:.*]] = %[[VAL_1]]) -> (i32)) { +// CHECK: %[[VAL_9:.*]] = arith.cmpi ne, %[[VAL_8]], %[[VAL_6]] : i32 +// CHECK: cc.condition %[[VAL_9]](%[[VAL_8]] : i32) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_10:.*]]: i32): +// CHECK: quake.x %[[VAL_3]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_10]] : i32 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_11:.*]]: i32): +// CHECK: %[[VAL_12:.*]] = arith.addi %[[VAL_11]], %[[VAL_2]] : i32 +// CHECK: cc.continue %[[VAL_12]] : i32 +// CHECK: } {normalized} +// CHECK: return %[[VAL_1]] : i32 +// CHECK: } + +// Dead loops: no unsigned value will ever be less than 0, so these loops will +// never execute. Make sure they are marked "dead" by the normalizer. + +__qpu__ void non_iterating_loop1() { + cudaq::qvector q(100); + for (std::uint64_t i = 1; i < 0; i++) + x(q[i]); +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_non_iterating_loop1._Z19non_iterating_loop1v() attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK: %[[VAL_0:.*]] = arith.constant false +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i64 +// CHECK: %[[VAL_2:.*]] = quake.alloca !quake.veq<100> +// CHECK: %[[VAL_3:.*]] = cc.loop while ((%[[VAL_4:.*]] = %[[VAL_1]]) -> (i64)) { +// CHECK: cc.condition %[[VAL_0]](%[[VAL_4]] : i64) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_5:.*]]: i64): +// CHECK: %[[VAL_6:.*]] = quake.extract_ref %[[VAL_2]]{{\[}}%[[VAL_5]]] : (!quake.veq<100>, i64) -> !quake.ref +// CHECK: quake.x %[[VAL_6]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_5]] : i64 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_7:.*]]: i64): +// CHECK: %[[VAL_8:.*]] = arith.addi %[[VAL_7]], %[[VAL_1]] : i64 +// CHECK: cc.continue %[[VAL_8]] : i64 +// CHECK: } {dead} +// CHECK: return +// CHECK: } + +__qpu__ int f1a() { + cudaq::qubit q; + for (unsigned u = 1; u < 0; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_f1a._Z3f1av() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK: %[[VAL_0:.*]] = arith.constant false +// CHECK: %[[VAL_1:.*]] = arith.constant 0 : i32 +// CHECK: %[[VAL_2:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_3:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_4:.*]] = cc.loop while ((%[[VAL_5:.*]] = %[[VAL_2]]) -> (i32)) { +// CHECK: cc.condition %[[VAL_0]](%[[VAL_5]] : i32) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_6:.*]]: i32): +// CHECK: quake.x %[[VAL_3]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_6]] : i32 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_7:.*]]: i32): +// CHECK: %[[VAL_8:.*]] = arith.addi %[[VAL_7]], %[[VAL_2]] : i32 +// CHECK: cc.continue %[[VAL_8]] : i32 +// CHECK: } {dead} +// CHECK: return %[[VAL_1]] : i32 +// CHECK: } + +__qpu__ int f1b() { + cudaq::qubit q; + for (unsigned u = 10; u < 0; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_f1b._Z3f1bv() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK: %[[VAL_0:.*]] = arith.constant false +// CHECK: %[[VAL_1:.*]] = arith.constant 1 : i32 +// CHECK: %[[VAL_2:.*]] = arith.constant 0 : i32 +// CHECK: %[[VAL_3:.*]] = arith.constant 10 : i32 +// CHECK: %[[VAL_4:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_5:.*]] = cc.loop while ((%[[VAL_6:.*]] = %[[VAL_3]]) -> (i32)) { +// CHECK: cc.condition %[[VAL_0]](%[[VAL_6]] : i32) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_7:.*]]: i32): +// CHECK: quake.x %[[VAL_4]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_7]] : i32 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_8:.*]]: i32): +// CHECK: %[[VAL_9:.*]] = arith.addi %[[VAL_8]], %[[VAL_1]] : i32 +// CHECK: cc.continue %[[VAL_9]] : i32 +// CHECK: } {dead} +// CHECK: return %[[VAL_2]] : i32 +// CHECK: } + +__qpu__ int f3() { + cudaq::qubit q; + for (std::uint64_t u = 22; u < 0; u++) + x(q); + return 0; +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__function_f3._Z2f3v() -> i32 attributes {"cudaq-entrypoint", "cudaq-kernel", no_this} { +// CHECK: %[[VAL_0:.*]] = arith.constant false +// CHECK: %[[VAL_1:.*]] = arith.constant 22 : i64 +// CHECK: %[[VAL_2:.*]] = arith.constant 1 : i64 +// CHECK: %[[VAL_3:.*]] = arith.constant 0 : i32 +// CHECK: %[[VAL_4:.*]] = quake.alloca !quake.ref +// CHECK: %[[VAL_5:.*]] = cc.loop while ((%[[VAL_6:.*]] = %[[VAL_1]]) -> (i64)) { +// CHECK: cc.condition %[[VAL_0]](%[[VAL_6]] : i64) +// CHECK: } do { +// CHECK: ^bb0(%[[VAL_7:.*]]: i64): +// CHECK: quake.x %[[VAL_4]] : (!quake.ref) -> () +// CHECK: cc.continue %[[VAL_7]] : i64 +// CHECK: } step { +// CHECK: ^bb0(%[[VAL_8:.*]]: i64): +// CHECK: %[[VAL_9:.*]] = arith.addi %[[VAL_8]], %[[VAL_2]] : i64 +// CHECK: cc.continue %[[VAL_9]] : i64 +// CHECK: } {dead} +// CHECK: return %[[VAL_3]] : i32 +// CHECK: } diff --git a/test/Quake/classical_optimization.qke b/test/Quake/classical_optimization.qke new file mode 100644 index 0000000000..b3bf994b76 --- /dev/null +++ b/test/Quake/classical_optimization.qke @@ -0,0 +1,253 @@ +// ========================================================================== // +// Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. // +// All rights reserved. // +// // +// This source code and the accompanying materials are made available under // +// the terms of the Apache License 2.0 which accompanies this distribution. // +// ========================================================================== // + +// RUN: cudaq-opt -classical-optimization-pipeline %s | FileCheck %s + +func.func @test_array_copy() -> i1 { + %c0_i64 = arith.constant 0 : i64 + %0 = cc.alloca !cc.array + %1 = cc.cast %0 : (!cc.ptr>) -> !cc.ptr + cc.store %c0_i64, %1 : !cc.ptr + %2 = cc.alloca !cc.array + %3 = cc.load %1 : !cc.ptr + %4 = cc.cast %2 : (!cc.ptr>) -> !cc.ptr + cc.store %3, %4 : !cc.ptr + %6 = cc.load %1 : !cc.ptr + %7 = cc.load %4 : !cc.ptr + %8 = arith.cmpi eq, %6, %7 : i64 + return %8 : i1 +} + +// CHECK-LABEL: func.func @test_array_copy() -> i1 { +// CHECK: %[[VAL_0:.*]] = arith.constant true +// CHECK: return %[[VAL_0]] : i1 +// CHECK: } + +func.func @test_nested_loop_unroll() { + %c1_i64 = arith.constant 1 : i64 + %c2_i64 = arith.constant 2 : i64 + %c0_i64 = arith.constant 0 : i64 + %0 = quake.alloca !quake.veq<6> + %1 = quake.extract_ref %0[0] : (!quake.veq<6>) -> !quake.ref + quake.x %1 : (!quake.ref) -> () + %2 = math.absi %c2_i64 : i64 + %3 = cc.alloca i64[%2 : i64] + %4:2 = cc.loop while ((%arg0 = %c0_i64, %arg1 = %c0_i64) -> (i64, i64)) { + %25 = arith.cmpi slt, %arg0, %c2_i64 : i64 + cc.condition %25(%arg0, %arg1 : i64, i64) + } do { + ^bb0(%arg0: i64, %arg1: i64): + %25 = cc.compute_ptr %3[%arg1] : (!cc.ptr>, i64) -> !cc.ptr + cc.store %arg1, %25 : !cc.ptr + %26 = arith.addi %arg1, %c1_i64 : i64 + cc.continue %arg0, %26 : i64, i64 + } step { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25, %arg1 : i64, i64 + } {invariant} + %5 = cc.alloca i64[%c2_i64 : i64] + %6 = cc.loop while ((%arg0 = %c0_i64) -> (i64)) { + %25 = arith.cmpi slt, %arg0, %c2_i64 : i64 + cc.condition %25(%arg0 : i64) + } do { + ^bb0(%arg0: i64): + %25 = cc.compute_ptr %3[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + %26 = cc.load %25 : !cc.ptr + %27 = arith.muli %26, %c2_i64 : i64 + %28 = cc.compute_ptr %5[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + cc.store %27, %28 : !cc.ptr + cc.continue %arg0 : i64 + } step { + ^bb0(%arg0: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25 : i64 + } {invariant} + %7 = cc.stdvec_init %5, %c2_i64 : (!cc.ptr>, i64) -> !cc.stdvec + %8 = cc.stdvec_size %7 : (!cc.stdvec) -> i64 + %9 = arith.subi %8, %c1_i64 : i64 + %10:2 = cc.loop while ((%arg0 = %c0_i64, %arg1 = %c0_i64) -> (i64, i64)) { + %25 = arith.cmpi slt, %arg0, %9 : i64 + cc.condition %25(%arg0, %arg1 : i64, i64) + } do { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + %26:2 = cc.loop while ((%arg2 = %25, %arg3 = %arg1) -> (i64, i64)) { + %27 = arith.cmpi slt, %arg2, %8 : i64 + cc.condition %27(%arg2, %arg3 : i64, i64) + } do { + ^bb0(%arg2: i64, %arg3: i64): + %27 = arith.addi %arg3, %c1_i64 : i64 + cc.continue %arg2, %27 : i64, i64 + } step { + ^bb0(%arg2: i64, %arg3: i64): + %27 = arith.addi %arg2, %c1_i64 : i64 + cc.continue %27, %arg3 : i64, i64 + } {invariant} + cc.continue %arg0, %26#1 : i64, i64 + } step { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25, %arg1 : i64, i64 + } {invariant} + %11 = math.absi %10#1 : i64 + %12 = cc.alloca i64[%11 : i64] + %13:2 = cc.loop while ((%arg0 = %c0_i64, %arg1 = %c0_i64) -> (i64, i64)) { + %25 = arith.cmpi slt, %arg0, %10#1 : i64 + cc.condition %25(%arg0, %arg1 : i64, i64) + } do { + ^bb0(%arg0: i64, %arg1: i64): + %25 = cc.compute_ptr %12[%arg1] : (!cc.ptr>, i64) -> !cc.ptr + cc.store %arg1, %25 : !cc.ptr + %26 = arith.addi %arg1, %c1_i64 : i64 + cc.continue %arg0, %26 : i64, i64 + } step { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25, %arg1 : i64, i64 + } {invariant} + %14 = cc.alloca i64[%10#1 : i64] + %15 = cc.loop while ((%arg0 = %c0_i64) -> (i64)) { + %25 = arith.cmpi slt, %arg0, %10#1 : i64 + cc.condition %25(%arg0 : i64) + } do { + ^bb0(%arg0: i64): + %25 = cc.compute_ptr %14[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + cc.store %c0_i64, %25 : !cc.ptr + cc.continue %arg0 : i64 + } step { + ^bb0(%arg0: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25 : i64 + } {invariant} + %16 = cc.stdvec_init %14, %10#1 : (!cc.ptr>, i64) -> !cc.stdvec + %17 = cc.alloca i64[%11 : i64] + %18:2 = cc.loop while ((%arg0 = %c0_i64, %arg1 = %c0_i64) -> (i64, i64)) { + %25 = arith.cmpi slt, %arg0, %10#1 : i64 + cc.condition %25(%arg0, %arg1 : i64, i64) + } do { + ^bb0(%arg0: i64, %arg1: i64): + %25 = cc.compute_ptr %17[%arg1] : (!cc.ptr>, i64) -> !cc.ptr + cc.store %arg1, %25 : !cc.ptr + %26 = arith.addi %arg1, %c1_i64 : i64 + cc.continue %arg0, %26 : i64, i64 + } step { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25, %arg1 : i64, i64 + } {invariant} + %19 = cc.alloca i64[%10#1 : i64] + %20 = cc.loop while ((%arg0 = %c0_i64) -> (i64)) { + %25 = arith.cmpi slt, %arg0, %10#1 : i64 + cc.condition %25(%arg0 : i64) + } do { + ^bb0(%arg0: i64): + %25 = cc.compute_ptr %19[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + cc.store %c0_i64, %25 : !cc.ptr + cc.continue %arg0 : i64 + } step { + ^bb0(%arg0: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25 : i64 + } {invariant} + %21 = cc.stdvec_init %19, %10#1 : (!cc.ptr>, i64) -> !cc.stdvec + %22:2 = cc.loop while ((%arg0 = %c0_i64, %arg1 = %c0_i64) -> (i64, i64)) { + %25 = arith.cmpi slt, %arg0, %9 : i64 + cc.condition %25(%arg0, %arg1 : i64, i64) + } do { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + %26:2 = cc.loop while ((%arg2 = %25, %arg3 = %arg1) -> (i64, i64)) { + %27 = arith.cmpi slt, %arg2, %8 : i64 + cc.condition %27(%arg2, %arg3 : i64, i64) + } do { + ^bb0(%arg2: i64, %arg3: i64): + %27 = cc.stdvec_data %16 : (!cc.stdvec) -> !cc.ptr> + %28 = cc.compute_ptr %27[%arg3] : (!cc.ptr>, i64) -> !cc.ptr + %29 = cc.stdvec_data %7 : (!cc.stdvec) -> !cc.ptr> + %30 = cc.compute_ptr %29[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + %31 = cc.load %30 : !cc.ptr + cc.store %31, %28 : !cc.ptr + %32 = cc.stdvec_data %21 : (!cc.stdvec) -> !cc.ptr> + %33 = cc.compute_ptr %32[%arg3] : (!cc.ptr>, i64) -> !cc.ptr + %34 = cc.compute_ptr %29[%arg2] : (!cc.ptr>, i64) -> !cc.ptr + %35 = cc.load %34 : !cc.ptr + cc.store %35, %33 : !cc.ptr + %36 = arith.addi %arg3, %c1_i64 : i64 + cc.continue %arg2, %36 : i64, i64 + } step { + ^bb0(%arg2: i64, %arg3: i64): + %27 = arith.addi %arg2, %c1_i64 : i64 + cc.continue %27, %arg3 : i64, i64 + } {invariant} + cc.continue %arg0, %26#1 : i64, i64 + } step { + ^bb0(%arg0: i64, %arg1: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25, %arg1 : i64, i64 + } {invariant} + %23 = cc.stdvec_size %16 : (!cc.stdvec) -> i64 + %24 = cc.loop while ((%arg0 = %c0_i64) -> (i64)) { + %25 = arith.cmpi slt, %arg0, %23 : i64 + cc.condition %25(%arg0 : i64) + } do { + ^bb0(%arg0: i64): + %25 = cc.stdvec_data %16 : (!cc.stdvec) -> !cc.ptr> + %26 = cc.compute_ptr %25[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + %27 = cc.load %26 : !cc.ptr + %28 = cc.stdvec_data %21 : (!cc.stdvec) -> !cc.ptr> + %29 = cc.compute_ptr %28[%arg0] : (!cc.ptr>, i64) -> !cc.ptr + %30 = cc.load %29 : !cc.ptr + %31 = arith.cmpi slt, %27, %30 : i64 + %32:2 = cc.if(%31) -> (i64, i64) { + cc.continue %27, %30 : i64, i64 + } else { + %34 = arith.cmpi sgt, %27, %30 : i64 + %35:2 = cc.if(%34) -> (i64, i64) { + cc.continue %30, %27 : i64, i64 + } else { + cc.continue %c0_i64, %c0_i64 : i64, i64 + } + cc.continue %35#0, %35#1 : i64, i64 + } + %33 = cc.loop while ((%arg1 = %32#0) -> (i64)) { + %34 = arith.cmpi slt, %arg1, %32#1 : i64 + cc.condition %34(%arg1 : i64) + } do { + ^bb0(%arg1: i64): + %34 = quake.extract_ref %0[%arg1] : (!quake.veq<6>, i64) -> !quake.ref + %35 = arith.addi %arg1, %c1_i64 : i64 + %36 = quake.extract_ref %0[%35] : (!quake.veq<6>, i64) -> !quake.ref + quake.x [%34] %36 : (!quake.ref, !quake.ref) -> () + cc.continue %arg1 : i64 + } step { + ^bb0(%arg1: i64): + %34 = arith.addi %arg1, %c1_i64 : i64 + cc.continue %34 : i64 + } {invariant} + cc.continue %arg0 : i64 + } step { + ^bb0(%arg0: i64): + %25 = arith.addi %arg0, %c1_i64 : i64 + cc.continue %25 : i64 + } {invariant} + return +} + +// CHECK-LABEL: func.func @test_nested_loop_unroll() { +// CHECK: %[[VAL_0:.*]] = quake.alloca !quake.veq<6> +// CHECK: %[[VAL_1:.*]] = quake.extract_ref %[[VAL_0]][0] : (!quake.veq<6>) -> !quake.ref +// CHECK: quake.x %[[VAL_1]] : (!quake.ref) -> () +// CHECK: %[[VAL_2:.*]] = quake.extract_ref %[[VAL_0]][0] : (!quake.veq<6>) -> !quake.ref +// CHECK: %[[VAL_3:.*]] = quake.extract_ref %[[VAL_0]][1] : (!quake.veq<6>) -> !quake.ref +// CHECK: quake.x [%[[VAL_2]]] %[[VAL_3]] : (!quake.ref, !quake.ref) -> () +// CHECK: %[[VAL_4:.*]] = quake.extract_ref %[[VAL_0]][1] : (!quake.veq<6>) -> !quake.ref +// CHECK: %[[VAL_5:.*]] = quake.extract_ref %[[VAL_0]][2] : (!quake.veq<6>) -> !quake.ref +// CHECK: quake.x [%[[VAL_4]]] %[[VAL_5]] : (!quake.ref, !quake.ref) -> () +// CHECK: return +// CHECK: } diff --git a/test/Quake/qir_api_branching.qke b/test/Quake/qir_api_branching.qke new file mode 100644 index 0000000000..3a0dfb8c44 --- /dev/null +++ b/test/Quake/qir_api_branching.qke @@ -0,0 +1,96 @@ +// ========================================================================== // +// Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. // +// All rights reserved. // +// // +// This source code and the accompanying materials are made available under // +// the terms of the Apache License 2.0 which accompanies this distribution. // +// ========================================================================== // + +// RUN: cudaq-opt -convert-to-qir-api %s | FileCheck %s + +func.func @__nvqpp__mlirgen__kernel() attributes {"cudaq-entrypoint", "cudaq-kernel"} { + %c0_i64 = arith.constant 0 : i64 + %c6_i64 = arith.constant 6 : i64 + %c1_i64 = arith.constant 1 : i64 + %c-1_i64 = arith.constant -1 : i64 + %c4_i64 = arith.constant 4 : i64 + %0 = quake.alloca !quake.veq<12> + %1 = cc.alloca !cc.array + %2 = cc.cast %1 : (!cc.ptr>) -> !cc.ptr + cc.store %c6_i64, %2 : !cc.ptr + %3 = cc.load %2 : !cc.ptr + %4 = quake.extract_ref %0[4] : (!quake.veq<12>) -> !quake.ref + quake.h %4 : (!quake.ref) -> () + %5 = arith.subi %c4_i64, %3 : i64 + %6 = arith.divsi %5, %c-1_i64 : i64 + %7 = arith.cmpi sgt, %6, %c0_i64 : i64 + %8 = arith.select %7, %6, %c0_i64 : i64 + cf.br ^bb1(%c0_i64 : i64) +^bb1(%9: i64): // 2 preds: ^bb0, ^bb2 + %10 = arith.cmpi ne, %9, %8 : i64 + cf.cond_br %10, ^bb2(%9 : i64), ^bb3(%4 : !quake.ref) +^bb2(%11: i64): // pred: ^bb1 + %12 = arith.muli %11, %c-1_i64 : i64 + %13 = arith.addi %3, %12 : i64 + %14 = arith.subi %13, %c1_i64 : i64 + %15 = quake.extract_ref %0[%14] : (!quake.veq<12>, i64) -> !quake.ref + %16 = quake.extract_ref %0[%13] : (!quake.veq<12>, i64) -> !quake.ref + quake.x [%15] %16 : (!quake.ref, !quake.ref) -> () + %17 = arith.addi %11, %c1_i64 : i64 + cf.br ^bb1(%17 : i64) +^bb3(%18: !quake.ref): // pred: ^bb1 + %19 = quake.extract_ref %0[2] : (!quake.veq<12>) -> !quake.ref + quake.x [%19] %18 : (!quake.ref, !quake.ref) -> () + quake.dealloc %0 : !quake.veq<12> + return +} + +// CHECK-LABEL: func.func @__nvqpp__mlirgen__kernel() attributes {"cudaq-entrypoint", "cudaq-kernel", "qir-api"} { +// CHECK-DAG: %[[VAL_0:.*]] = arith.constant 2 : i64 +// CHECK-DAG: %[[VAL_1:.*]] = constant @__quantum__qis__x__ctl : (!cc.ptr>, !cc.ptr>) -> () +// CHECK-DAG: %[[VAL_2:.*]] = arith.constant 0 : i64 +// CHECK-DAG: %[[VAL_3:.*]] = arith.constant 6 : i64 +// CHECK-DAG: %[[VAL_4:.*]] = arith.constant 1 : i64 +// CHECK-DAG: %[[VAL_5:.*]] = arith.constant -1 : i64 +// CHECK-DAG: %[[VAL_6:.*]] = arith.constant 4 : i64 +// CHECK-DAG: %[[VAL_7:.*]] = arith.constant 12 : i64 +// CHECK: %[[VAL_8:.*]] = call @__quantum__rt__qubit_allocate_array(%[[VAL_7]]) : (i64) -> !cc.ptr> +// CHECK: %[[VAL_9:.*]] = cc.alloca !cc.array +// CHECK: %[[VAL_10:.*]] = cc.cast %[[VAL_9]] : (!cc.ptr>) -> !cc.ptr +// CHECK: cc.store %[[VAL_3]], %[[VAL_10]] : !cc.ptr +// CHECK: %[[VAL_11:.*]] = cc.load %[[VAL_10]] : !cc.ptr +// CHECK: %[[VAL_12:.*]] = call @__quantum__rt__array_get_element_ptr_1d(%[[VAL_8]], %[[VAL_6]]) : (!cc.ptr>, i64) -> !cc.ptr>> +// CHECK: %[[VAL_13:.*]] = cc.load %[[VAL_12]] : !cc.ptr>> +// CHECK: call @__quantum__qis__h(%[[VAL_13]]) : (!cc.ptr>) -> () +// CHECK: %[[VAL_14:.*]] = arith.subi %[[VAL_6]], %[[VAL_11]] : i64 +// CHECK: %[[VAL_15:.*]] = arith.divsi %[[VAL_14]], %[[VAL_5]] : i64 +// CHECK: %[[VAL_16:.*]] = arith.cmpi sgt, %[[VAL_15]], %[[VAL_2]] : i64 +// CHECK: %[[VAL_17:.*]] = arith.select %[[VAL_16]], %[[VAL_15]], %[[VAL_2]] : i64 +// CHECK: cf.br ^bb1(%[[VAL_2]] : i64) +// CHECK: ^bb1(%[[VAL_18:.*]]: i64): +// CHECK: %[[VAL_19:.*]] = arith.cmpi ne, %[[VAL_18]], %[[VAL_17]] : i64 +// CHECK: cf.cond_br %[[VAL_19]], ^bb2(%[[VAL_18]] : i64), ^bb3(%[[VAL_13]] : !cc.ptr>) +// CHECK: ^bb2(%[[VAL_20:.*]]: i64): +// CHECK: %[[VAL_21:.*]] = arith.muli %[[VAL_20]], %[[VAL_5]] : i64 +// CHECK: %[[VAL_22:.*]] = arith.addi %[[VAL_11]], %[[VAL_21]] : i64 +// CHECK: %[[VAL_23:.*]] = arith.subi %[[VAL_22]], %[[VAL_4]] : i64 +// CHECK: %[[VAL_24:.*]] = call @__quantum__rt__array_get_element_ptr_1d(%[[VAL_8]], %[[VAL_23]]) : (!cc.ptr>, i64) -> !cc.ptr>> +// CHECK: %[[VAL_25:.*]] = cc.load %[[VAL_24]] : !cc.ptr>> +// CHECK: %[[VAL_26:.*]] = call @__quantum__rt__array_get_element_ptr_1d(%[[VAL_8]], %[[VAL_22]]) : (!cc.ptr>, i64) -> !cc.ptr>> +// CHECK: %[[VAL_27:.*]] = cc.load %[[VAL_26]] : !cc.ptr>> +// CHECK: %[[VAL_28:.*]] = cc.cast %[[VAL_25]] : (!cc.ptr>) -> !llvm.ptr +// CHECK: %[[VAL_29:.*]] = cc.func_ptr %[[VAL_1]] : ((!cc.ptr>, !cc.ptr>) -> ()) -> !llvm.ptr +// CHECK: %[[VAL_30:.*]] = cc.cast %[[VAL_27]] : (!cc.ptr>) -> !llvm.ptr +// CHECK: cc.call_vararg @generalizedInvokeWithRotationsControlsTargets(%[[VAL_2]], %[[VAL_2]], %[[VAL_4]], %[[VAL_4]], %[[VAL_29]], %[[VAL_28]], %[[VAL_30]]) : (i64, i64, i64, i64, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> () +// CHECK: %[[VAL_31:.*]] = arith.addi %[[VAL_20]], %[[VAL_4]] : i64 +// CHECK: cf.br ^bb1(%[[VAL_31]] : i64) +// CHECK: ^bb3(%[[VAL_32:.*]]: !cc.ptr>): +// CHECK: %[[VAL_33:.*]] = call @__quantum__rt__array_get_element_ptr_1d(%[[VAL_8]], %[[VAL_0]]) : (!cc.ptr>, i64) -> !cc.ptr>> +// CHECK: %[[VAL_34:.*]] = cc.load %[[VAL_33]] : !cc.ptr>> +// CHECK: %[[VAL_35:.*]] = cc.cast %[[VAL_34]] : (!cc.ptr>) -> !llvm.ptr +// CHECK: %[[VAL_36:.*]] = cc.func_ptr %[[VAL_1]] : ((!cc.ptr>, !cc.ptr>) -> ()) -> !llvm.ptr +// CHECK: %[[VAL_37:.*]] = cc.cast %[[VAL_32]] : (!cc.ptr>) -> !llvm.ptr +// CHECK: cc.call_vararg @generalizedInvokeWithRotationsControlsTargets(%[[VAL_2]], %[[VAL_2]], %[[VAL_4]], %[[VAL_4]], %[[VAL_36]], %[[VAL_35]], %[[VAL_37]]) : (i64, i64, i64, i64, !llvm.ptr, !llvm.ptr, !llvm.ptr) -> () +// CHECK: call @__quantum__rt__qubit_release_array(%[[VAL_8]]) : (!cc.ptr>) -> () +// CHECK: return +// CHECK: } diff --git a/test/Quake/roundtrip-ops.qke b/test/Quake/roundtrip-ops.qke index 39fb636319..806939d526 100644 --- a/test/Quake/roundtrip-ops.qke +++ b/test/Quake/roundtrip-ops.qke @@ -811,6 +811,23 @@ func.func @indirect_callable1(%arg : !cc.indirect_callable<() -> ()>) { // CHECK: return // CHECK: } +func.func @varargs_test() { + %1 = arith.constant 12 : i32 + %2 = cc.undef !cc.ptr + cc.call_vararg @my_variadic(%1, %2) : (i32, !cc.ptr) -> () + return +} + +llvm.func @my_variadic(i32, ...) + +// CHECK-LABEL: func.func @varargs_test() { +// CHECK: %[[VAL_0:.*]] = arith.constant 12 : i32 +// CHECK: %[[VAL_1:.*]] = cc.undef !cc.ptr +// CHECK: cc.call_vararg @my_variadic(%[[VAL_0]], %[[VAL_1]]) : (i32, !cc.ptr) -> () +// CHECK: return +// CHECK: } +// CHECK: llvm.func @my_variadic(i32, ...) + func.func @indirect_callable2(%arg : !cc.indirect_callable<(i32) -> i64>) -> i64 { %cst = arith.constant 4 : i32 %0 = cc.call_indirect_callable %arg, %cst : (!cc.indirect_callable<(i32) -> i64>, i32) -> i64 diff --git a/tools/nvqpp/nvq++.in b/tools/nvqpp/nvq++.in index ee62e38388..ae6a6ab265 100644 --- a/tools/nvqpp/nvq++.in +++ b/tools/nvqpp/nvq++.in @@ -587,7 +587,7 @@ while [ $# -ne 0 ]; do *.o | *.so | *.bundle) OBJS="${OBJS} $1" ;; - *.cpp | *.cc) + *.cpp | *.cc | *.cxx | *.c++) SRCS="${SRCS} $1" ;; *.a | *.dylib) @@ -746,7 +746,8 @@ if ${SHOW_VERSION} && [ -z "$SRCS" ] && [ -z "$OBJS" ]; then fi for i in ${SRCS}; do - file=$(basename -s .cc -s .cpp $i) + file_with_suffix=$(basename $i) + file=${file_with_suffix%.*} # If LIBRARY_MODE explicitly requested, then # simply compile with the classical compiler. diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 2843ce5bcf..f350873afc 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -391,59 +391,6 @@ endif() add_subdirectory(backends) add_subdirectory(Optimizer) -set(CUDAQ_BRAKET_RUNTIME_TEST_SOURCES - # Integration tests - integration/adjoint_tester.cpp - integration/builder_tester.cpp - integration/ccnot_tester.cpp - integration/draw_tester.cpp - integration/ghz_nisq_tester.cpp - integration/gradient_tester.cpp - integration/grover_test.cpp - integration/nlopt_tester.cpp - integration/qpe_ftqc.cpp - integration/qpe_nisq.cpp - integration/qubit_allocation.cpp - integration/vqe_tester.cpp - integration/bug67_vqe_then_sample.cpp - integration/bug77_vqe_with_shots.cpp - integration/bug116_cusv_measure_bug.cpp - integration/async_tester.cpp - integration/negative_controls_tester.cpp - integration/observe_result_tester.cpp - integration/noise_tester.cpp - integration/get_state_tester.cpp - integration/kernels_tester.cpp - common/MeasureCountsTester.cpp - common/NoiseModelTester.cpp - integration/gate_library_tester.cpp -) -set(TEST_EXE_NAME "test_runtime_braket") -set(NVQIR_BACKEND "braket") -set(NVQIR_BACKEND_NAME "braket") -add_executable(${TEST_EXE_NAME} main.cpp ${CUDAQ_BRAKET_RUNTIME_TEST_SOURCES} "") -target_compile_definitions(${TEST_EXE_NAME} PRIVATE -DNVQIR_BACKEND_NAME=braket) -target_compile_definitions(${TEST_EXE_NAME} PRIVATE __MATH_LONG_DOUBLE_CONSTANTS) -target_include_directories(${TEST_EXE_NAME} PRIVATE .) -if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT APPLE) - target_link_options(${TEST_EXE_NAME} PRIVATE -Wl,--no-as-needed) -endif() -target_link_libraries(${TEST_EXE_NAME} - PUBLIC - nvqir-qpp nvqir - cudaq fmt::fmt-header-only - cudaq-platform-default - cudaq-rest-qpu - cudaq-builder - gtest_main - $ENV{CUQUANTUM_INSTALL_PREFIX}/lib/libcudensitymat.so.0) -set(TEST_LABELS "") -if ("${TEST_LABELS}" STREQUAL "") - gtest_discover_tests(${TEST_EXE_NAME}) -else() - gtest_discover_tests(${TEST_EXE_NAME} PROPERTIES LABELS "${TEST_LABELS}") -endif() - if (CUDAQ_ENABLE_PYTHON) if (NOT Python_FOUND) message(FATAL_ERROR "find_package(Python) not run?") diff --git a/utils/mock_qpu/iqm/__init__.py b/utils/mock_qpu/iqm/__init__.py index 8c34b73386..d81b547ba3 100644 --- a/utils/mock_qpu/iqm/__init__.py +++ b/utils/mock_qpu/iqm/__init__.py @@ -237,7 +237,7 @@ def _simulate_circuit(instructions: list[iqm_client.Instruction], measurement_qubits_positions) probabilities = np.diag(partial_trace) return { - ms: int(prob * shots) for ms, prob in zip( + ms: int(round(prob * shots)) for ms, prob in zip( _generate_measurement_strings(len(measurement_qubits_positions)), probabilities, ) From 4b47201da52809730c58faaea4b5e734967035d7 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 18 Feb 2025 11:58:27 +0000 Subject: [PATCH 300/311] removing some files that should not be part of this PR Signed-off-by: Bettina Heim --- docs/sphinx/api/languages/cpp_api.rst | 47 - main.cpp | 452 -------- out.txt | 1367 +++++++++++++++++++++++++ python/cudaq/operator/manipulation.py | 24 - v1.perf | 104 -- v2.perf | 108 -- v3.perf | 104 -- 7 files changed, 1367 insertions(+), 839 deletions(-) delete mode 100644 main.cpp create mode 100644 out.txt delete mode 100644 v1.perf delete mode 100644 v2.perf delete mode 100644 v3.perf diff --git a/docs/sphinx/api/languages/cpp_api.rst b/docs/sphinx/api/languages/cpp_api.rst index 79065bebb2..34487dbefb 100644 --- a/docs/sphinx/api/languages/cpp_api.rst +++ b/docs/sphinx/api/languages/cpp_api.rst @@ -223,53 +223,6 @@ Utilities .. doxygentypedef:: cudaq::real .. doxygenfunction:: cudaq::range(std::size_t) - -Dynamics -========= - -.. .. doxygenclass:: cudaq::EvolveResult - :members: - -.. .. doxygenclass:: cudaq::AsyncEvolveResult - :members: - -.. doxygenclass:: cudaq::operator_sum - :members: - -.. doxygenclass:: cudaq::product_operator - :members: - -.. doxygenclass:: cudaq::scalar_operator - :members: - -.. doxygenclass:: cudaq::matrix_operator - :members: - -.. doxygenclass:: cudaq::OperatorArithmetics - :members: - -.. doxygenclass:: cudaq::MatrixArithmetics - :members: - -.. doxygenclass:: cudaq::Schedule - :members: - -.. doxygenclass:: cudaq::operators - :members: - -.. doxygenclass:: cudaq::pauli - :members: - -.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, bool store_intermediate_states) - -.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) - -.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, state initial_state, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) - -.. .. doxygenfunction:: cudaq::evolve(Operator hamiltonian, std::map dimensions, Schedule schedule, std::vector initial_states, std::vector collapse_operators, std::vector observables, bool store_intermediate_states) - -.. .. doxygenfunction:: cudaq::evolve_async - Namespaces =========== diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 4c43968376..0000000000 --- a/main.cpp +++ /dev/null @@ -1,452 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include -#include -#include -#include -#include - -int main() { - - bool run_old = false; - bool run_new = true; - - std::vector repetitions = {100, 1000, 10000}; - - // multiplication inplace with itself - std::cout << std::endl << "multiplication inplace with itself" << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = - cudaq::spin_operator::identity(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - std::cout << std::endl << "multiplication inplace with other" << std::endl; - // multiplication inplace with other - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = - cudaq::spin_operator::identity(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // multiplication inplace with other (reverse order) - std::cout << std::endl - << "multiplication inplace with other (reverse order)" << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::product_operator prod_op = - cudaq::spin_operator::identity(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= cudaq::spin::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - prod_op *= cudaq::spin_operator::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // addition inplace with itself - std::cout << std::endl << "addition inplace with itself" << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(0); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // addition inplace with other - std::cout << std::endl << "addition inplace with other" << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - if (nr_reps > 1000) - std::cout << "Old setup takes minutes - skipped" << std::endl; - else { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // addition inplace with other (reverse order) - std::cout << std::endl - << "addition inplace with other (reverse order)" << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - if (nr_reps > 1000) - std::cout << "Old setup takes minutes - skipped" << std::endl; - else { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += cudaq::spin::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += cudaq::spin_operator::x(nr_reps - i); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // addition inplace with product self - std::cout << std::endl << "addition inplace with product self" << std::endl; - - for (auto nr_reps : repetitions) { - - auto spin_prod = cudaq::spin_op(); - for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(i); - cudaq::spin_op spin_op = spin_prod; - auto prod_op = cudaq::spin_operator::identity(); - for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(i); - cudaq::operator_sum op_sum = prod_op; - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // addition inplace with product self (reverse order) - std::cout << std::endl - << "addition inplace with product self (reverse order)" - << std::endl; - - for (auto nr_reps : repetitions) { - - auto spin_prod = cudaq::spin_op(); - for (auto i = 0; i < 100; ++i) - spin_prod *= cudaq::spin::x(100 - i); - cudaq::spin_op spin_op = spin_prod; - auto prod_op = cudaq::spin_operator::identity(); - for (auto i = 0; i < 100; ++i) - prod_op *= cudaq::spin_operator::x(100 - i); - cudaq::operator_sum op_sum = prod_op; - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op += spin_prod; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum += prod_op; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // product inplace with 2-term sum on fixed degrees - std::cout << std::endl - << "product inplace with 2-term sum on fixed degrees" << std::endl; - - for (auto nr_reps : repetitions) { - - auto spin_term = cudaq::spin::x(0) + cudaq::spin::y(1); - cudaq::spin_op spin_op = spin_term; - auto prod_term = cudaq::spin_operator::x(0) + cudaq::spin_operator::y(1); - cudaq::operator_sum op_sum = prod_term; - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= spin_term; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= prod_term; - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - repetitions = {10, 20}; // we run out of memory if we go to 30 here - - // product inplace with 2-term sum on varying degrees - std::cout << std::endl - << "product inplace with 2-term sum on varying degrees" - << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = cudaq::spin_operator::empty(); - op_sum = cudaq::spin_operator::identity(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(i) + cudaq::spin::z(i + 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(i) + cudaq::spin_operator::z(i + 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // product inplace with 2-term sum on varying degrees (reverse order) - std::cout - << std::endl - << "product inplace with 2-term sum on varying degrees (reverse order)" - << std::endl; - - for (auto nr_reps : repetitions) { - - cudaq::spin_op spin_op = cudaq::spin_op(); - cudaq::operator_sum op_sum = - cudaq::spin_operator::empty(); // fixme: only const & constructor is - // public - op_sum = cudaq::spin_operator::identity(); - - std::cout << "nr ops: " << nr_reps << std::endl; - - if (run_old) { - /* - start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - spin_op *= (cudaq::spin::x(nr_reps - i) + cudaq::spin::z(nr_reps - i - - 1)); stop = std::chrono::high_resolution_clock::now(); duration = - std::chrono::duration(stop - start); std::cout << "Old setup took - " << duration.count() << " seconds.\n"; - */ - std::cout << "Old setup segfaults" << std::endl; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_reps; ++i) - op_sum *= (cudaq::spin_operator::x(nr_reps - i) + - cudaq::spin_operator::z(nr_reps - i - 1)); - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - - // sum of products of random terms - std::cout << std::endl << "sum of products of random terms" << std::endl; - - std::vector nr_sum_terms = {10, 100, 1000}; - std::vector nr_product_terms = {100, 1000, 10000}; - auto nr_degrees = 100; - std::vector spin_ops; - for (auto i = 0; i < nr_degrees; ++i) { - spin_ops.push_back(cudaq::spin::x(i)); - spin_ops.push_back(cudaq::spin::y(i)); - spin_ops.push_back(cudaq::spin::z(i)); - spin_ops.push_back(cudaq::spin::i(i)); - } - std::vector> leaf_ops; - for (auto i = 0; i < nr_degrees; ++i) { - leaf_ops.push_back(cudaq::spin_operator::x(i)); - leaf_ops.push_back(cudaq::spin_operator::y(i)); - leaf_ops.push_back(cudaq::spin_operator::z(i)); - leaf_ops.push_back(cudaq::spin_operator::i(i)); - } - srand(5); // random number seed - - for (auto nr_sums : nr_sum_terms) { - for (auto nr_prods : nr_product_terms) { - - std::vector> indices; - for (auto i = 0; i < nr_sums; ++i) { - indices.push_back({}); - for (auto j = 0; j < nr_prods; ++j) - indices[i].push_back(rand() % 400); - } - - std::cout << "nr terms " << nr_sums << ", term length " << nr_prods - << std::endl; - - auto spin_op = cudaq::spin_op(); - auto op_sum = cudaq::spin_operator::empty(); - - if (run_old) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_sums; ++i) { - auto term = cudaq::spin_op(); - for (auto j = 0; j < nr_prods; ++j) - term *= spin_ops[indices[i][j]]; - spin_op += term; - } - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "Old setup took " << duration.count() << " seconds.\n"; - } - - if (run_new) { - auto start = std::chrono::high_resolution_clock::now(); - for (auto i = 0; i < nr_sums; ++i) { - auto term = cudaq::spin_operator::identity(); - for (auto j = 0; j < nr_prods; ++j) - term *= leaf_ops[indices[i][j]]; - op_sum += term; - } - auto stop = std::chrono::high_resolution_clock::now(); - auto duration = std::chrono::duration(stop - start); - std::cout << "New setup took " << duration.count() << " seconds.\n"; - } - } - } - return 0; -} \ No newline at end of file diff --git a/out.txt b/out.txt new file mode 100644 index 0000000000..2e9b041217 --- /dev/null +++ b/out.txt @@ -0,0 +1,1367 @@ +Build directory: /workspaces/cuda-quantum/build/release +CUDA version 12.0 detected. +Using cuQuantum installation in /usr/local/lib/python3.10/dist-packages/cuquantum. +Using cuTensor installation in /usr/local/lib/python3.10/dist-packages/cutensor. +Configuring nvq++ to use the lld linker by default. +Preparing CUDA-Q build with LLVM installation in /usr/local/llvm... +-- Submodule update +-- CUDA Quantum static linking disabled. +-- Using LLVMConfig.cmake in: /usr/local/llvm/lib/cmake/llvm +-- Using ClangConfig.cmake in: /usr/local/llvm/lib/cmake/clang +-- Using MLIRConfig.cmake in: /usr/local/llvm/lib/cmake/mlir +-- Build type is Release +-- MPI CXX Found: /usr/local/openmpi/bin/mpiexec +-- Building default MPI Comm plugin +-- Module support is disabled. +-- Version: 8.1.2 +-- Build type: Release +-- CXX_STANDARD: 20 +-- Required features: cxx_variadic_templates +-- Build spdlog: 1.11.0 +-- Build type: Release +-- Using system curl for CPR. +-- C++ Requests CMake Options +-- ======================================================= +-- CPR_GENERATE_COVERAGE: OFF +-- CPR_CURL_NOSIGNAL: OFF +-- CPR_USE_SYSTEM_GTEST: OFF +-- CPR_FORCE_USE_SYSTEM_CURL: TRUE +-- CPR_ENABLE_SSL: ON +-- CPR_FORCE_OPENSSL_BACKEND: ON +-- CPR_FORCE_WINSSL_BACKEND: OFF +-- CPR_FORCE_DARWINSSL_BACKEND: OFF +-- CPR_FORCE_MBEDTLS_BACKEND: OFF +-- CPR_ENABLE_LINTING: OFF +-- CPR_ENABLE_CPPCHECK: OFF +-- CPR_BUILD_TESTS: OFF +-- CPR_BUILD_TESTS_SSL: OFF +-- ======================================================= +-- Disabled SSL backend auto detect since either CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, or CPR_FORCE_WINSSL_BACKEND is enabled. +-- Using OpenSSL. +-- Curl 8.5.0 found on this system. +-- OpenMP Found. Adding interface definitions to target libqpp. +-- Cuda language found. +-- Building with -fPIC +-- Building with -fPIC +-- Building with -fPIC +-- OpenSSL Found, building REST Client. +-- OpenMP Found. Adding build flags to target nvqir-qpp: -fopenmp. +-- OpenMP Found. Adding build flags to target nvqir-dm: -fopenmp. +-- Found Python: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter Development Development.Module Development.Embed +-- Could NOT find pybind11 (missing: pybind11_DIR) +-- CUSTATEVEC_ROOT and CUDA_FOUND - building custatevec NVQIR backend. +-- CUSTATEVEC_LIB: /usr/local/lib/python3.10/dist-packages/cuquantum/lib/libcustatevec.so.1 +-- CUTENSOR_LIB: /usr/local/lib/python3.10/dist-packages/cutensor/lib/libcutensor.so.2 +-- CUTENSORNET_LIB: /usr/local/lib/python3.10/dist-packages/cuquantum/lib/libcutensornet.so.2 +-- CUTENSORNET_INC: /usr/local/lib/python3.10/dist-packages/cuquantum/include/cutensornet.h +-- Found cutensornet version: 2.6.0 +-- cudensitymat header: /usr/local/lib/python3.10/dist-packages/cuquantum/include/cudensitymat.h +-- OpenMP Found. Adding build flags to target cudaq-spin: -fopenmp. +-- Building with -fPIC +-- OpenMP Found. Adding build flags to target cudaq-em-photonics: -fopenmp. +-- OpenMP Found. Adding build flags to target cudaq-ensmallen: -fopenmp. +-- Building with -fPIC +-- Building REST QPU. +-- Found AWS SDK for C++, Version: 1.11.454, Install Root:/usr/local/aws, Platform Prefix:, Platform Dependent Libraries: pthread;curl +-- Components specified for AWSSDK: braket;s3-crt;sts, application will be depending on libs: aws-cpp-sdk-braket;aws-cpp-sdk-s3-crt;aws-cpp-sdk-sts;aws-cpp-sdk-core;aws-crt-cpp;aws-c-http;aws-c-mqtt;aws-c-cal;aws-c-auth;aws-c-common;aws-c-io;aws-checksums;aws-c-event-stream;aws-c-s3;aws-c-compression;aws-c-sdkutils;pthread;curl +-- Try finding aws-cpp-sdk-core +-- LibCrypto Include Dir: /usr/local/openssl/include +-- LibCrypto Shared Lib: crypto_SHARED_LIBRARY-NOTFOUND +-- LibCrypto Static Lib: crypto_STATIC_LIBRARY-NOTFOUND +-- Found aws-cpp-sdk-core +-- Try finding aws-cpp-sdk-sts +-- Found aws-cpp-sdk-sts +-- Try finding aws-cpp-sdk-s3-crt +-- Found aws-cpp-sdk-s3-crt +-- Try finding aws-cpp-sdk-braket +-- Found aws-cpp-sdk-braket +-- Found AWS SDK for C++, Version: 1.11.454, Install Root:/usr/local/aws, Platform Prefix:, Platform Dependent Libraries: pthread;curl +-- Components specified for AWSSDK: braket;s3-crt;sts, application will be depending on libs: aws-cpp-sdk-braket;aws-cpp-sdk-s3-crt;aws-cpp-sdk-sts;aws-cpp-sdk-core;aws-crt-cpp;aws-c-http;aws-c-mqtt;aws-c-cal;aws-c-auth;aws-c-common;aws-c-io;aws-checksums;aws-c-event-stream;aws-c-s3;aws-c-compression;aws-c-sdkutils;pthread;curl +-- Try finding aws-cpp-sdk-core +-- LibCrypto Include Dir: /usr/local/openssl/include +-- LibCrypto Shared Lib: crypto_SHARED_LIBRARY-NOTFOUND +-- LibCrypto Static Lib: crypto_STATIC_LIBRARY-NOTFOUND +-- Found aws-cpp-sdk-core +-- Try finding aws-cpp-sdk-sts +-- Found aws-cpp-sdk-sts +-- Try finding aws-cpp-sdk-s3-crt +-- Found aws-cpp-sdk-s3-crt +-- Try finding aws-cpp-sdk-braket +-- Found aws-cpp-sdk-braket +-- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) +-- Minizip found: /usr/local/zlib/lib/libminizip.a and /usr/local/zlib/include/minizip/unzip.h +-- Building ORCA REST QPU. +-- Building Fermioniq REST QPU. +-- Building QuEra REST QPU. +-- Building with -fPIC +-- OpenMP Found. Adding build flags to target cudaq-chemistry: -fopenmp. +-- OpenMP Found. Adding build flags to target cudaq-operator: -fopenmp. +-- Building with -fPIC +-- Building with -fPIC +-- Found Python: /usr/bin/python3.10 (found suitable version "3.10.12", minimum required is "3") found components: Interpreter Development Development.Module Development.Embed +-- pybind11 v2.12.0 dev1 +-- Building with -fPIC +-- Building cudaq-pyscf plugin. +-- Pytest and Numpy found, building Python tests. +-- CUDAQ_TEST_MOCK_SERVERS=TRUE and dependent modules available, building Python remote QPU tests. +-- Building cutensornet backend tests. +-- Building with -fPIC +-- Registering CustomPassPlugin as a pass plugin (static build: OFF) +-- Found Python: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter +-- OpenMP Found. Adding build flags to target nvqir-qpp-observe-test: -fopenmp. +-- Building with -fPIC +-- OpenFermion PySCF found, enabling chemistry tests. +-- Configuring done (11.1s) +-- Generating done (0.3s) +-- Build files have been written to: /workspaces/cuda-quantum/build/release +Building CUDA-Q with configuration Release... +[1/565] Building Passes.h.inc... +[2/565] Building Passes.h.inc... +[3/565] Building CodeGenOps.cpp.inc... +[4/565] Building CodeGenOps.h.inc... +[5/565] Building CCOps.cpp.inc... +[6/565] Building CCOps.h.inc... +[7/565] Building CXX object runtime/cudaq/distributed/builtin/CMakeFiles/cudaq-comm-plugin.dir/mpi_comm_impl.cpp.o +[8/565] Linking CXX shared library lib/plugins/libcudaq-comm-plugin.so +[9/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/Trace.cpp.o +[10/565] Building CXX object runtime/nvqir/CMakeFiles/nvqir.dir/__/cudaq/qis/state.cpp.o +[11/565] Building CXX object runtime/nvqir/CMakeFiles/nvqir.dir/QIRTypes.cpp.o +[12/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/Executor.cpp.o +[13/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/ServerHelper.cpp.o +[14/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/Future.cpp.o +[15/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/NoiseModel.cpp.o +[16/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/infleqtion/CMakeFiles/cudaq-serverhelper-infleqtion.dir/InfleqtionServerHelper.cpp.o +[17/565] Building CXX object lib/Optimizer/Builder/CMakeFiles/obj.OptimBuilder.dir/Intrinsics.cpp.o +[18/565] Building CXX object lib/Optimizer/Builder/CMakeFiles/obj.OptimBuilder.dir/Factory.cpp.o +[19/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/CodeGenDialect.cpp.o +[20/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/CodeGenOps.cpp.o +[21/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/CCToLLVM.cpp.o +[22/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertCCToLLVM.cpp.o +[23/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToExecMgr.cpp.o +[24/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/Pipelines.cpp.o +[25/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/Passes.cpp.o +[26/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToQIR.cpp.o +[27/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToQIRProfile.cpp.o +[28/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/QuakeToCodegen.cpp.o +[29/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/RemoveMeasurements.cpp.o +[30/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/QuakeToExecMgr.cpp.o +[31/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertDecl.cpp.o +[32/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertStmt.cpp.o +[33/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/QuakeToLLVM.cpp.o +[34/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertType.cpp.o +[35/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/VerifyNVQIRCalls.cpp.o +[36/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/TranslateToOpenQASM.cpp.o +[37/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertExpr.cpp.o +[38/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ASTBridge.cpp.o +[39/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/VerifyQIRProfile.cpp.o +[40/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToQIRAPI.cpp.o +[41/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/AggressiveEarlyInlining.cpp.o +[42/565] Building CXX object lib/Optimizer/Dialect/CC/CMakeFiles/obj.CCDialect.dir/CCDialect.cpp.o +[43/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ApplyControlNegations.cpp.o +[44/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/AddDeallocs.cpp.o +[45/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/WireSetsToProfileQIR.cpp.o +[46/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ApplyOpSpecialization.cpp.o +[47/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ArgumentSynthesis.cpp.o +[48/565] Building CXX object lib/Optimizer/Dialect/CC/CMakeFiles/obj.CCDialect.dir/CCOps.cpp.o +[49/565] Linking CXX static library lib/libCCDialect.a +[50/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ClassicalOptimization.cpp.o +[51/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/Decomposition.cpp.o +[52/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/CombineQuantumAlloc.cpp.o +[53/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ConstPropComplex.cpp.o +[54/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/CombineMeasurements.cpp.o +[55/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DelayMeasurements.cpp.o +[56/565] Building CXX object lib/Optimizer/Dialect/Quake/CMakeFiles/obj.QuakeDialect.dir/QuakeOps.cpp.o +[57/565] Linking CXX static library lib/libQuakeDialect.a +[58/565] Linking CXX static library lib/libOptimBuilder.a +[59/565] Linking CXX static library lib/libcudaq-mlirgen.a +[60/565] Linking CXX static library lib/libOptCodeGen.a +[61/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DecompositionPatterns.cpp.o +[62/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/BasisConversion.cpp.o +[63/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/EraseNopCalls.cpp.o +[64/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DeleteStates.cpp.o +[65/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ExpandMeasurements.cpp.o +[66/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/FactorQuantumAlloc.cpp.o +[67/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ExpandControlVeqs.cpp.o +[68/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GetConcreteMatrix.cpp.o +[69/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GenDeviceCodeLoader.cpp.o +[70/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GlobalizeArrayValues.cpp.o +[71/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopAnalysis.cpp.o +[72/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LinearCtrlRelations.cpp.o +[73/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GenKernelExecution.cpp.o +[74/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LiftArrayAlloc.cpp.o +[75/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LambdaLifting.cpp.o +[76/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopPeeling.cpp.o +[77/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopNormalize.cpp.o +[78/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/MultiControlDecomposition.cpp.o +[79/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopUnroll.cpp.o +[80/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LowerToCFG.cpp.o +[81/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LowerUnwind.cpp.o +[82/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/Mapping.cpp.o +[83/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ObserveAnsatz.cpp.o +[84/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/PySynthCallableBlockArgs.cpp.o +[85/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DependencyAnalysis.cpp.o +[86/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/PruneCtrlRelations.cpp.o +[87/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/MemToReg.cpp.o +[88/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/QuakeAddMetadata.cpp.o +[89/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/RefToVeqAlloc.cpp.o +[90/565] Linking CXX shared library lib/libcudaq-common.so +/usr/bin/ld: missing --end-group; added as last command line option +[91/565] Linking CXX shared library lib/libcudaq-serverhelper-infleqtion.so +[92/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/QuakeSynthesizer.cpp.o +[93/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/StatePreparation.cpp.o +[94/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/UpdateRegisterNames.cpp.o +[95/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/WriteAfterWriteElimination.cpp.o +[96/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/RegToMem.cpp.o +[97/565] Building CXX object runtime/common/CMakeFiles/cudaq-mlir-runtime.dir/ArgumentConversion.cpp.o +[98/565] Building CXX object runtime/nvqir/CMakeFiles/nvqir.dir/NVQIR.cpp.o +[99/565] Linking CXX shared library lib/libnvqir.so +[100/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/WiresToWiresets.cpp.o +[101/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-quera-qpu.dir/QuEraServerHelper.cpp.o +[102/565] Building CXX object runtime/nvqir/stim/CMakeFiles/nvqir-stim.dir/StimCircuitSimulator.cpp.o +[103/565] Linking CXX shared library lib/libnvqir-stim.so +[104/565] Building CXX object runtime/nvqir/custatevec/CMakeFiles/nvqir-custatevec-fp64.dir/CuStateVecCircuitSimulator.cpp.o +[105/565] Linking CXX shared library lib/libnvqir-custatevec-fp64.so +[106/565] Building CXX object runtime/nvqir/custatevec/CMakeFiles/nvqir-custatevec-fp32.dir/CuStateVecCircuitSimulatorF32.cpp.o +[107/565] Linking CXX shared library lib/libnvqir-custatevec-fp32.so +[108/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/tensornet_spin_op.cpp.o +[109/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/tensornet_state.cpp.o +[110/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/UnitarySynthesis.cpp.o +[111/565] Linking CXX static library lib/libOptTransforms.a +[112/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/tn_simulation_state.cpp.o +[113/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/simulator_cutensornet.cpp.o +[114/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-quera-qpu.dir/QuEraRemoteRESTQPU.cpp.o +[115/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/tensornet_spin_op.cpp.o +[116/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/simulator_tensornet_register.cpp.o +[117/565] Building CXX object runtime/nvqir/qpp/CMakeFiles/nvqir-qpp.dir/QppCircuitSimulator.cpp.o +[118/565] Linking CXX shared library lib/libnvqir-qpp.so +[119/565] Building CXX object runtime/common/CMakeFiles/cudaq-mlir-runtime.dir/RuntimeMLIR.cpp.o +[120/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/tensornet_state.cpp.o +[121/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/tensornet-mpi-util.dir/mpi_support.cpp.o +[122/565] Building CXX object runtime/nvqir/qpp/CMakeFiles/nvqir-dm.dir/QppDMCircuitSimulator.cpp.o +[123/565] Linking CXX shared library lib/libnvqir-dm.so +[124/565] Building CXX object runtime/nvqir/cudensitymat/CMakeFiles/nvqir-dynamics.dir/mpi_support.cpp.o +[125/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/simulator_cutensornet.cpp.o +[126/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/qis/execution_manager_c_api.cpp.o +[127/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/utils/cudaq_utils.cpp.o +[128/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/utils/tensor.cpp.o +[129/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/cudaq.cpp.o +[130/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/qis/state.cpp.o +[131/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/algorithms/draw.cpp.o +[132/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/simulator_mps_register.cpp.o +[133/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/platform/quantum_platform.cpp.o +[134/565] Building CXX object runtime/nvqir/cudensitymat/CMakeFiles/nvqir-dynamics.dir/CuDensityMatSim.cpp.o +[135/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/qis/remote_state.cpp.o +[136/565] Linking CXX shared library lib/libnvqir-dynamics.so +[137/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/distributed/mpi_plugin.cpp.o +[138/565] Building CXX object runtime/cudaq/qis/managers/default/CMakeFiles/cudaq-em-default.dir/DefaultExecutionManager.cpp.o +[139/565] Linking CXX shared library lib/libcudaq-em-default.so +[140/565] Building CXX object runtime/cudaq/platform/default/CMakeFiles/cudaq-platform-default.dir/DefaultQuantumPlatform.cpp.o +[141/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/ionq/IonQServerHelper.cpp.o +[142/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/infleqtion/InfleqtionServerHelper.cpp.o +[143/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/quantinuum/QuantinuumServerHelper.cpp.o +[144/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/anyon/AnyonServerHelper.cpp.o +[145/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/oqc/OQCServerHelp.cpp.o +[146/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/iqm/IQMServerHelper.cpp.o +[147/565] Linking CXX executable unittests/Optimizer/OptimizerUnitTests +[148/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/braket/BraketExecutor.cpp.o +[149/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/braket/BraketServerHelper.cpp.o +[150/565] Building CXX object runtime/cudaq/qis/managers/photonics/CMakeFiles/cudaq-em-photonics.dir/PhotonicsExecutionManager.cpp.o +[151/565] Linking CXX shared library lib/libcudaq-em-photonics.so +[152/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/RemoteRESTQPU.cpp.o +[153/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/ionq/CMakeFiles/cudaq-serverhelper-ionq.dir/IonQServerHelper.cpp.o +[154/565] Linking CXX shared library lib/libcudaq-serverhelper-ionq.so +[155/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/anyon/CMakeFiles/cudaq-serverhelper-anyon.dir/AnyonServerHelper.cpp.o +[156/565] Linking CXX shared library lib/libcudaq-serverhelper-anyon.so +[157/565] Building CXX object unittests/backends/quera/CMakeFiles/test_quera.dir/JsonPayloadTester.cpp.o +[158/565] Linking CXX executable unittests/backends/quera/test_quera +[159/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/iqm/CMakeFiles/cudaq-serverhelper-iqm.dir/IQMServerHelper.cpp.o +[160/565] Linking CXX shared library lib/libcudaq-serverhelper-iqm.so +[161/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaQPU.cpp.o +[162/565] Building CXX object unittests/Optimizer/CMakeFiles/test_quake_synth.dir/QuakeSynthTester.cpp.o +[163/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/oqc/CMakeFiles/cudaq-serverhelper-oqc.dir/OQCServerHelp.cpp.o +[164/565] Linking CXX shared library lib/libcudaq-serverhelper-oqc.so +[165/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/quantinuum/CMakeFiles/cudaq-serverhelper-quantinuum.dir/QuantinuumServerHelper.cpp.o +[166/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaExecutor.cpp.o +[167/565] Linking CXX shared library lib/libcudaq-serverhelper-quantinuum.so +[168/565] Building CXX object runtime/cudaq/platform/mqpu/custatevec/CMakeFiles/gpu-emulated-qpu.dir/GPUEmulatedQPU.cpp.o +[169/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/braket/CMakeFiles/cudaq-serverhelper-braket.dir/BraketServerHelper.cpp.o +[170/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaRemoteRESTQPU.cpp.o +[171/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/braket/CMakeFiles/cudaq-serverhelper-braket.dir/BraketExecutor.cpp.o +[172/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaServerHelper.cpp.o +[173/565] Linking CXX shared library lib/libcudaq-serverhelper-braket.so +[174/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/mps_simulation_state.cpp.o +[175/565] Building CXX object runtime/cudaq/platform/mqpu/CMakeFiles/cudaq-platform-mqpu.dir/MultiQPUPlatform.cpp.o +[176/565] Building CXX object runtime/cudaq/platform/mqpu/remote/CMakeFiles/cudaq-remote-simulator-qpu.dir/RemoteSimulatorQPU.cpp.o +[177/565] Building CXX object runtime/cudaq/platform/fermioniq/CMakeFiles/cudaq-fermioniq-qpu.dir/helpers/FermioniqServerHelper.cpp.o +[178/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.cc +[179/565] Building CXX object runtime/cudaq/platform/fermioniq/helpers/CMakeFiles/cudaq-serverhelper-fermioniq.dir/FermioniqServerHelper.cpp.o +[180/565] Linking CXX shared library lib/libcudaq-serverhelper-fermioniq.so +[181/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-quera-qpu.dir/QuEraExecutor.cpp.o +[182/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/callback.cpp.o +[183/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-serverhelper-quera.dir/QuEraExecutor.cpp.o +[184/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/spin_operators.cpp.o +[185/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/boson_operators.cpp.o +[186/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-serverhelper-quera.dir/QuEraServerHelper.cpp.o +[187/565] Linking CXX shared library lib/libcudaq-serverhelper-quera.so +[188/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/scalar_operators.cpp.o +[189/565] Building CXX object runtime/cudaq/builder/CMakeFiles/cudaq-builder.dir/QuakeValue.cpp.o +[190/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/manipulation.cpp.o +[191/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/matrix_operators.cpp.o +[192/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/helpers.cpp.o +[193/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/schedule.cpp.o +[194/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/product_operators.cpp.o +[195/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/operator_sum.cpp.o +[196/565] Linking CXX shared library lib/libcudaq-operator.so +[197/565] Linking CXX shared library lib/libcudaq.so +[198/565] Building CXX object runtime/test/CMakeFiles/test_argument_conversion.dir/test_argument_conversion.cpp.o +[199/565] Linking CXX shared library lib/libnvqir-tensornet.so +[200/565] Linking CXX shared library lib/libnvqir-tensornet-mps.so +[201/565] Linking CXX shared library lib/libcudaq-platform-default.so +[202/565] Linking CXX shared library lib/libcudaq-platform-mqpu.so +[203/565] Linking CXX executable bin/cudaq-lsp-server +[204/565] Building CXX object runtime/cudaq/platform/default/rest_server/CMakeFiles/rest-remote-platform-client.dir/helpers/RestRemoteClient.cpp.o +[205/565] Building CXX object tools/cudaq-opt/CMakeFiles/cudaq-opt.dir/cudaq-opt.cpp.o +[206/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.quake.ops_gen +[207/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.quake +[208/565] Linking CXX executable bin/CircuitCheck +[209/565] cd /workspaces/cuda-quantum/build/release/python && /usr/local/cmake-3.26/bin/cmake -E copy_directory /workspaces/cuda-quantum/python /workspaces/cuda-quantum/build/release/python && /usr/local/cmake-3.26/bin/cmake -DMETADATA_FILE="/workspaces/cuda-quantum/build/release/python/cudaq/_metadata.py" -DCUDA_VERSION_MAJOR=12 -P /workspaces/cuda-quantum/python/metadata.cmake +-- Creating metadata file in /workspaces/cuda-quantum/build/release/python/cudaq/_metadata.py. +[210/565] Building CXX object runtime/cudaq/builder/CMakeFiles/cudaq-builder.dir/kernel_builder.cpp.o +[211/565] Linking CXX executable bin/cudaq-opt +[212/565] Building CXX object tools/cudaq-translate/CMakeFiles/cudaq-translate.dir/cudaq-translate.cpp.o +[213/565] Building CXX object tools/cudaq-qpud/CMakeFiles/cudaq-qpud.dir/RestServerMain.cpp.o +[214/565] Linking CXX shared library lib/libcudaq-mlir-runtime.so +[215/565] Linking CXX shared library lib/libcudaq-rest-qpu.so +[216/565] Linking CXX shared library lib/librest-remote-platform-client.so +[217/565] Building CXX object runtime/cudaq/platform/fermioniq/CMakeFiles/cudaq-fermioniq-qpu.dir/FermioniqQPU.cpp.o +[218/565] Linking CXX shared library lib/libcudaq-orca-qpu.so +[219/565] Linking CXX shared library lib/libcudaq-remote-simulator-qpu.so +[220/565] Linking CXX shared library lib/libcudaq-fermioniq-qpu.so +[221/565] Linking CXX shared library lib/libcudaq-quera-qpu.so +[222/565] Linking CXX shared library lib/libcudaq-builder.so +[223/565] Linking CXX executable bin/test_argument_conversion +[224/565] Building CXX object runtime/cudaq/platform/default/rest_server/CMakeFiles/rest-remote-platform-server.dir/helpers/RestRemoteServer.cpp.o +[225/565] Linking CXX executable bin/cudaq-translate +[226/565] Linking CXX shared library lib/librest-remote-platform-server.so +[227/565] Linking CXX executable bin/cudaq-qpud +[228/565] Building CXX object python/utils/CMakeFiles/cudaq-py-utils.dir/LinkedLibraryHolder.cpp.o +[229/565] Linking CXX shared library lib/libcudaq-py-utils.so +[230/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_draw.cpp.o +[231/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_AnalogHamiltonian.cpp.o +[232/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_optimizer.cpp.o +[233/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_observe_async.cpp.o +[234/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_sample_async.cpp.o +[235/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_translate.cpp.o +[236/565] Building CXX object tools/cudaq-quake/CMakeFiles/cudaq-quake.dir/cudaq-quake.cpp.o +[237/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/qis/py_qubit_qis.cpp.o +[238/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/target/py_testing_utils.cpp.o +[239/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_state.cpp.o +[240/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_evolve.cpp.o +[241/565] Linking CXX executable bin/cudaq-quake +[242/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_vqe.cpp.o +[243/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyRemoteSimulatorQPU.cpp.o +[244/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaQPU.cpp.o +[245/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/utils/LinkedLibraryHolder.cpp.o +[246/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaExecutor.cpp.o +[247/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.async_dialect.ops_gen +[248/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.async_dialect +[249/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.bufferization +[250/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.bufferization.ops_gen +[251/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.builtin +[252/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.builtin.ops_gen +[253/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.complex +[254/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.complex.ops_gen +[255/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.cf +[256/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.cf.ops_gen +[257/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.func +[258/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.func.ops_gen +[259/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.gpu +[260/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.gpu.ops_gen +[261/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.linalg.ops_gen +[262/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.linalg +[263/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaServerHelper.cpp.o +[264/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaRemoteRESTQPU.cpp.o +[265/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/common/ArgumentConversion.cpp.o +[266/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/mlir/py_register_dialects.cpp.o +[267/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyFermioniqRESTQPU.cpp.o +[268/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyQuEraRemoteRESTQPU.cpp.o +[269/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.transform +[270/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.transform.ops_gen +[271/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.loop_transform +[272/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.loop_transform.ops_gen +[273/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.structured_transform +[274/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.structured_transform.ops_gen +[275/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.math +[276/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.math.ops_gen +[277/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.arith +[278/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.arith.ops_gen +[279/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.memref +[280/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.memref.ops_gen +[281/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.ml_program +[282/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.ml_program.ops_gen +[283/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.quant +[284/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.pdl +[285/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.pdl.ops_gen +[286/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.scf +[287/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.scf.ops_gen +[288/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.shape +[289/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.shape.ops_gen +[290/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.sparse_tensor +[291/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.sparse_tensor.ops_gen +[292/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tensor +[293/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tensor.ops_gen +[294/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tosa +[295/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tosa.ops_gen +[296/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.vector +[297/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.vector.ops_gen +[298/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Core +[299/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.ExecutionEngine +[300/565] Building dialects/_cc_ops_gen.py... +[301/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.cc.ops_gen +[302/565] Linking CXX shared library lib/plugins/libcudaq-pyscf.so +[303/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/platform/py_alt_launch_kernel.cpp.o +[304/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_ExecutionContext.cpp.o +[305/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_EvolveResult.cpp.o +[306/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_NoiseModel.cpp.o +[307/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_ObserveResult.cpp.o +[308/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyRestRemoteClient.cpp.o +[309/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/adjoint_tester.cpp.o +[310/565] Building CXX object python/tests/interop/CMakeFiles/cudaq_test_cpp_algo.dir/test_cpp_quantum_algorithm_module.cpp.o +[311/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/ccnot_tester.cpp.o +[312/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/CUDAQuantumExtension.cpp.o +[313/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/deuteron_variational_tester.cpp.o +[314/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/draw_tester.cpp.o +[315/565] Linking CXX shared library python/cudaq/mlir/_mlir_libs/libCUDAQuantumPythonCAPI.so +[316/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/ghz_nisq_tester.cpp.o +[317/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/gradient_tester.cpp.o +[318/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/grover_test.cpp.o +[319/565] Building CXX object python/runtime/interop/CMakeFiles/cudaq-python-interop.dir/PythonCppInterop.cpp.o +[320/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/qpe_ftqc.cpp.o +[321/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyRemoteRESTQPU.cpp.o +[322/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/nlopt_tester.cpp.o +[323/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/qpe_nisq.cpp.o +[324/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirRegisterEverything.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[325/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirAsyncPasses.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[326/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/builder_tester.cpp.o +[327/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirLinalgPasses.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[328/565] Linking CXX shared library lib/libcudaq-python-interop.so +[329/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirGPUPasses.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[330/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsLinalg.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[331/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsTransform.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 5 LTRANS jobs +[332/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsPDL.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 5 LTRANS jobs +[333/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsQuant.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 6 LTRANS jobs +[334/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirSparseTensorPasses.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[335/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/qubit_allocation.cpp.o +[336/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsSparseTensor.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 6 LTRANS jobs +[337/565] Linking CXX shared module python/tests/interop/cudaq_test_cpp_algo.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 4 LTRANS jobs +[338/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/bug67_vqe_then_sample.cpp.o +[339/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 5 LTRANS jobs +[340/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/bug77_vqe_with_shots.cpp.o +[341/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/bug116_cusv_measure_bug.cpp.o +[342/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/vqe_tester.cpp.o +[343/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/noise_tester.cpp.o +[344/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/negative_controls_tester.cpp.o +[345/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/async_tester.cpp.o +[346/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/get_state_tester.cpp.o +[347/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/observe_result_tester.cpp.o +[348/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/measure_reset_tester.cpp.o +[349/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/qir/NVQIRTester.cpp.o +[350/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/common/MeasureCountsTester.cpp.o +[351/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/kernels_tester.cpp.o +[352/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/qpe_ftqc.cpp.o +[353/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/common/NoiseModelTester.cpp.o +[354/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/tracer_tester.cpp.o +[355/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/gate_library_tester.cpp.o +[356/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/qis/QubitQISTester.cpp.o +[357/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/adjoint_tester.cpp.o +[358/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/ccnot_tester.cpp.o +[359/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/draw_tester.cpp.o +[360/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/deuteron_variational_tester.cpp.o +[361/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/gradient_tester.cpp.o +[362/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/ghz_nisq_tester.cpp.o +[363/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/grover_test.cpp.o +[364/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/nlopt_tester.cpp.o +[365/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/vqe_tester.cpp.o +[366/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/qpe_nisq.cpp.o +[367/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/bug67_vqe_then_sample.cpp.o +[368/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/qubit_allocation.cpp.o +[369/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/async_tester.cpp.o +[370/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/bug77_vqe_with_shots.cpp.o +[371/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/bug116_cusv_measure_bug.cpp.o +[372/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/builder_tester.cpp.o +[373/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/negative_controls_tester.cpp.o +[374/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/observe_result_tester.cpp.o +[375/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/measure_reset_tester.cpp.o +[376/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/qis/QubitQISTester.cpp.o +[377/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/qir/NVQIRTester.cpp.o +[378/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/get_state_tester.cpp.o +[379/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/common/MeasureCountsTester.cpp.o +[380/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/kernels_tester.cpp.o +[381/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/noise_tester.cpp.o +[382/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/gate_library_tester.cpp.o +[383/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/common/NoiseModelTester.cpp.o +[384/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/tracer_tester.cpp.o +[385/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlir.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 34 LTRANS jobs +[386/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/ccnot_tester.cpp.o +[387/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/deuteron_variational_tester.cpp.o +[388/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/adjoint_tester.cpp.o +[389/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/gradient_tester.cpp.o +[390/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/draw_tester.cpp.o +[391/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/grover_test.cpp.o +[392/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/ghz_nisq_tester.cpp.o +[393/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/nlopt_tester.cpp.o +[394/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/qpe_ftqc.cpp.o +[395/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/qpe_nisq.cpp.o +[396/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/builder_tester.cpp.o +[397/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/qubit_allocation.cpp.o +[398/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/vqe_tester.cpp.o +[399/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/bug67_vqe_then_sample.cpp.o +[400/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/bug77_vqe_with_shots.cpp.o +[401/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/bug116_cusv_measure_bug.cpp.o +[402/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/observe_result_tester.cpp.o +[403/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/async_tester.cpp.o +[404/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/negative_controls_tester.cpp.o +[405/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/get_state_tester.cpp.o +[406/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/measure_reset_tester.cpp.o +[407/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/qir/NVQIRTester.cpp.o +[408/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/noise_tester.cpp.o +[409/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/common/MeasureCountsTester.cpp.o +[410/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/qis/QubitQISTester.cpp.o +[411/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/kernels_tester.cpp.o +[412/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/common/NoiseModelTester.cpp.o +[413/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/gate_library_tester.cpp.o +[414/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/backends/QPPTester.cpp.o +[415/565] Linking CXX executable unittests/test_runtime_qpp +[416/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/tracer_tester.cpp.o +[417/565] Linking CXX executable unittests/test_runtime_stim +[418/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/adjoint_tester.cpp.o +[419/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/ccnot_tester.cpp.o +[420/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/draw_tester.cpp.o +[421/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/deuteron_variational_tester.cpp.o +[422/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/gradient_tester.cpp.o +[423/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/ghz_nisq_tester.cpp.o +[424/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/grover_test.cpp.o +[425/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/nlopt_tester.cpp.o +[426/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/qpe_ftqc.cpp.o +[427/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/qpe_nisq.cpp.o +[428/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/qubit_allocation.cpp.o +[429/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/bug77_vqe_with_shots.cpp.o +[430/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/bug116_cusv_measure_bug.cpp.o +[431/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/bug67_vqe_then_sample.cpp.o +[432/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/vqe_tester.cpp.o +[433/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_quakeDialects.cpython-310-x86_64-linux-gnu.so +lto-wrapper: warning: using serial compilation of 54 LTRANS jobs +[434/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/async_tester.cpp.o +[435/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/negative_controls_tester.cpp.o +[436/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/noise_tester.cpp.o +[437/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/builder_tester.cpp.o +[438/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/measure_reset_tester.cpp.o +[439/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/qir/NVQIRTester.cpp.o +[440/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/get_state_tester.cpp.o +[441/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/observe_result_tester.cpp.o +[442/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/common/MeasureCountsTester.cpp.o +[443/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/kernels_tester.cpp.o +[444/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/common/NoiseModelTester.cpp.o +[445/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/tracer_tester.cpp.o +[446/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/gate_library_tester.cpp.o +[447/565] Building CXX object unittests/CMakeFiles/test_mqpu.dir/mqpu/mqpu_tester.cpp.o +[448/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/backends/QPPDMTester.cpp.o +[449/565] Linking CXX executable unittests/test_mqpu +[450/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/deuteron_variational_tester.cpp.o +[451/565] Linking CXX executable unittests/test_runtime_dm +[452/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/gradient_tester.cpp.o +[453/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/qis/QubitQISTester.cpp.o +[454/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/nlopt_tester.cpp.o +[455/565] Linking CXX executable unittests/test_runtime_custatevec-fp32 +[456/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/adjoint_tester.cpp.o +[457/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/draw_tester.cpp.o +[458/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/gradient_tester.cpp.o +[459/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/ccnot_tester.cpp.o +[460/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/deuteron_variational_tester.cpp.o +[461/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/grover_test.cpp.o +[462/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/ghz_nisq_tester.cpp.o +[463/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/nlopt_tester.cpp.o +[464/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/qpe_ftqc.cpp.o +[465/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/vqe_tester.cpp.o +[466/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/bug67_vqe_then_sample.cpp.o +[467/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/qpe_nisq.cpp.o +[468/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/qubit_allocation.cpp.o +[469/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/builder_tester.cpp.o +[470/565] Linking CXX executable unittests/test_custatevec_observe_from_sampling +[471/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/bug77_vqe_with_shots.cpp.o +[472/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/bug116_cusv_measure_bug.cpp.o +[473/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/builder_tester.cpp.o +[474/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/negative_controls_tester.cpp.o +[475/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/async_tester.cpp.o +[476/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/measure_reset_tester.cpp.o +[477/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/qir/NVQIRTester.cpp.o +[478/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/get_state_tester.cpp.o +[479/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/observe_result_tester.cpp.o +[480/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/common/MeasureCountsTester.cpp.o +[481/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/kernels_tester.cpp.o +[482/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/noise_tester.cpp.o +[483/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/common/NoiseModelTester.cpp.o +[484/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/tracer_tester.cpp.o +[485/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/adjoint_tester.cpp.o +[486/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/gate_library_tester.cpp.o +[487/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/qis/QubitQISTester.cpp.o +[488/565] Linking CXX executable unittests/test_runtime_tensornet +[489/565] Building CUDA object unittests/CMakeFiles/test_gpu_get_state.dir/gpu/get_state_tester.cu.o +[490/565] Linking CXX executable unittests/test_gpu_get_state +[491/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/draw_tester.cpp.o +[492/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/gradient_tester.cpp.o +[493/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/ccnot_tester.cpp.o +[494/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/deuteron_variational_tester.cpp.o +[495/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/nlopt_tester.cpp.o +[496/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/ghz_nisq_tester.cpp.o +[497/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/grover_test.cpp.o +[498/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/qpe_ftqc.cpp.o +[499/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/vqe_tester.cpp.o +[500/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/bug67_vqe_then_sample.cpp.o +[501/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/qpe_nisq.cpp.o +[502/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/bug116_cusv_measure_bug.cpp.o +[503/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/bug77_vqe_with_shots.cpp.o +[504/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/qubit_allocation.cpp.o +[505/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/builder_tester.cpp.o +[506/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/negative_controls_tester.cpp.o +[507/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/async_tester.cpp.o +[508/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/measure_reset_tester.cpp.o +[509/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/get_state_tester.cpp.o +[510/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/observe_result_tester.cpp.o +[511/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/qir/NVQIRTester.cpp.o +[512/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/common/MeasureCountsTester.cpp.o +[513/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/noise_tester.cpp.o +[514/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/common/NoiseModelTester.cpp.o +[515/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/kernels_tester.cpp.o +[516/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/tracer_tester.cpp.o +[517/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/noise_tester.cpp.o +[518/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/utils.cpp.o +[519/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/gate_library_tester.cpp.o +[520/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/deuteron_variational_tester.cpp.o +[521/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/qis/QubitQISTester.cpp.o +[522/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/observe_result_tester.cpp.o +[523/565] Linking CXX executable unittests/test_runtime_tensornet-mps +[524/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/scalar_operator.cpp.o +[525/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/operator_conversions.cpp.o +[526/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/spin_operator.cpp.o +[527/565] Building CXX object unittests/CMakeFiles/test_qudit.dir/qudit/SimpleQuditTester.cpp.o +[528/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/matrix_operator.cpp.o +[529/565] Building CXX object unittests/CMakeFiles/test_utils.dir/utils/Tensor.cpp.o +[530/565] Building CXX object unittests/CMakeFiles/test_photonics.dir/photonics/PhotonicsTester.cpp.o +[531/565] Linking CXX executable unittests/test_photonics +[532/565] Building CXX object unittests/CMakeFiles/test_mpi_plugin.dir/mpi/mpi_tester.cpp.o +[533/565] Linking CXX executable unittests/test_mpi_plugin +[534/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/builder_tester.cpp.o +[535/565] Building CXX object unittests/CMakeFiles/test_domains.dir/domains/ChemistryTester.cpp.o +[536/565] Linking CXX executable unittests/test_domains +[537/565] Linking CXX executable unittests/test_tensornet_observe_path_reuse +[538/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/operator_sum.cpp.o +[539/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/product_operator.cpp.o +[540/565] Linking CXX executable unittests/test_operators +[541/565] Building CXX object unittests/backends/infleqtion/CMakeFiles/test_infleqtion.dir/InfleqtionTester.cpp.o +[542/565] Linking CXX executable unittests/backends/infleqtion/test_infleqtion +[543/565] Building CXX object unittests/backends/ionq/CMakeFiles/test_ionq.dir/IonQTester.cpp.o +[544/565] Building CXX object unittests/CMakeFiles/test_utils.dir/utils/UtilsTester.cpp.o +[545/565] Linking CXX executable unittests/test_utils +[546/565] Linking CXX executable unittests/backends/ionq/test_ionq +[547/565] Building CXX object unittests/backends/anyon/CMakeFiles/test_anyon.dir/AnyonTester.cpp.o +[548/565] Linking CXX executable unittests/Optimizer/test_quake_synth +[549/565] Linking CXX executable unittests/backends/anyon/test_anyon +[550/565] Building CXX object unittests/backends/braket/CMakeFiles/test_braket.dir/BraketTester.cpp.o +[551/565] Linking CXX executable unittests/backends/braket/test_braket +[552/565] Building CXX object unittests/backends/oqc/CMakeFiles/test_oqc.dir/OQCTester.cpp.o +[553/565] Linking CXX executable unittests/backends/oqc/test_oqc +[554/565] Building CXX object unittests/backends/iqm/CMakeFiles/test_iqm.dir/IQMTester.cpp.o +[555/565] Linking CXX executable unittests/backends/iqm/test_iqm +[556/565] Building CXX object unittests/backends/quantinuum/CMakeFiles/test_quantinuum.dir/QuantinuumTester.cpp.o +[557/565] Linking CXX executable unittests/backends/quantinuum/test_quantinuum +[558/565] Building CXX object unittests/qudit/simple_qudit/CMakeFiles/cudaq-em-qudit.dir/SimpleQuditExecutionManager.cpp.o +[559/565] Linking CXX shared library unittests/qudit/simple_qudit/libcudaq-em-qudit.so +[560/565] Linking CXX executable unittests/test_qudit +[561/565] Building CXX object unittests/backends/qpp_observe/CMakeFiles/nvqir-qpp-observe-test.dir/QPPObserveBackend.cpp.o +[562/565] Linking CXX shared library unittests/backends/qpp_observe/libnvqir-qpp-observe-test.so +[563/565] Building CXX object unittests/backends/qpp_observe/CMakeFiles/test_observe_backend.dir/QppObserveTester.cpp.o +[564/565] Linking CXX executable unittests/backends/qpp_observe/test_observe_backend +[564/565] Install the project... +-- Install configuration: "Release" +-- Installing: /usr/local/cudaq/lib/plugins/libcudaq-comm-plugin.so +-- Up-to-date: /usr/local/cudaq/lib/libfmt.a +-- Up-to-date: /usr/local/cudaq/include/fmt/args.h +-- Up-to-date: /usr/local/cudaq/include/fmt/chrono.h +-- Up-to-date: /usr/local/cudaq/include/fmt/color.h +-- Up-to-date: /usr/local/cudaq/include/fmt/compile.h +-- Up-to-date: /usr/local/cudaq/include/fmt/core.h +-- Up-to-date: /usr/local/cudaq/include/fmt/format.h +-- Up-to-date: /usr/local/cudaq/include/fmt/format-inl.h +-- Up-to-date: /usr/local/cudaq/include/fmt/os.h +-- Up-to-date: /usr/local/cudaq/include/fmt/ostream.h +-- Up-to-date: /usr/local/cudaq/include/fmt/printf.h +-- Up-to-date: /usr/local/cudaq/include/fmt/ranges.h +-- Up-to-date: /usr/local/cudaq/include/fmt/std.h +-- Up-to-date: /usr/local/cudaq/include/fmt/xchar.h +-- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-config.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-config-version.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-targets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-targets-release.cmake +-- Up-to-date: /usr/local/cudaq/lib/pkgconfig/fmt.pc +-- Up-to-date: /usr/local/cudaq/lib/libcpr.a +-- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprTargets-release.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprConfigVersion.cmake +-- Up-to-date: /usr/local/cudaq/include/cpr +-- Up-to-date: /usr/local/cudaq/include/cpr/verbose.h +-- Up-to-date: /usr/local/cudaq/include/cpr/error.h +-- Up-to-date: /usr/local/cudaq/include/cpr/payload.h +-- Up-to-date: /usr/local/cudaq/include/cpr/proxies.h +-- Up-to-date: /usr/local/cudaq/include/cpr/buffer.h +-- Up-to-date: /usr/local/cudaq/include/cpr/local_port.h +-- Up-to-date: /usr/local/cudaq/include/cpr/parameters.h +-- Up-to-date: /usr/local/cudaq/include/cpr/curl_container.h +-- Up-to-date: /usr/local/cudaq/include/cpr/bearer.h +-- Up-to-date: /usr/local/cudaq/include/cpr/low_speed.h +-- Up-to-date: /usr/local/cudaq/include/cpr/reserve_size.h +-- Up-to-date: /usr/local/cudaq/include/cpr/ssl_options.h +-- Up-to-date: /usr/local/cudaq/include/cpr/response.h +-- Up-to-date: /usr/local/cudaq/include/cpr/http_version.h +-- Up-to-date: /usr/local/cudaq/include/cpr/connect_timeout.h +-- Up-to-date: /usr/local/cudaq/include/cpr/redirect.h +-- Up-to-date: /usr/local/cudaq/include/cpr/timeout.h +-- Up-to-date: /usr/local/cudaq/include/cpr/interface.h +-- Up-to-date: /usr/local/cudaq/include/cpr/file.h +-- Up-to-date: /usr/local/cudaq/include/cpr/interceptor.h +-- Up-to-date: /usr/local/cudaq/include/cpr/body.h +-- Up-to-date: /usr/local/cudaq/include/cpr/session.h +-- Up-to-date: /usr/local/cudaq/include/cpr/cprtypes.h +-- Up-to-date: /usr/local/cudaq/include/cpr/callback.h +-- Up-to-date: /usr/local/cudaq/include/cpr/range.h +-- Up-to-date: /usr/local/cudaq/include/cpr/multipart.h +-- Up-to-date: /usr/local/cudaq/include/cpr/threadpool.h +-- Up-to-date: /usr/local/cudaq/include/cpr/accept_encoding.h +-- Up-to-date: /usr/local/cudaq/include/cpr/cookies.h +-- Up-to-date: /usr/local/cudaq/include/cpr/auth.h +-- Up-to-date: /usr/local/cudaq/include/cpr/proxyauth.h +-- Up-to-date: /usr/local/cudaq/include/cpr/local_port_range.h +-- Up-to-date: /usr/local/cudaq/include/cpr/user_agent.h +-- Up-to-date: /usr/local/cudaq/include/cpr/singleton.h +-- Up-to-date: /usr/local/cudaq/include/cpr/status_codes.h +-- Up-to-date: /usr/local/cudaq/include/cpr/cpr.h +-- Up-to-date: /usr/local/cudaq/include/cpr/api.h +-- Up-to-date: /usr/local/cudaq/include/cpr/ssl_ctx.h +-- Up-to-date: /usr/local/cudaq/include/cpr/limit_rate.h +-- Up-to-date: /usr/local/cudaq/include/cpr/cert_info.h +-- Up-to-date: /usr/local/cudaq/include/cpr/async.h +-- Up-to-date: /usr/local/cudaq/include/cpr/util.h +-- Up-to-date: /usr/local/cudaq/include/cpr/curlholder.h +-- Up-to-date: /usr/local/cudaq/include/cpr/unix_socket.h +-- Up-to-date: /usr/local/cudaq/include/cpr +-- Up-to-date: /usr/local/cudaq/include/cpr/cprver.h +-- Up-to-date: /usr/local/cudaq/include +-- Up-to-date: /usr/local/cudaq/include/crow +-- Up-to-date: /usr/local/cudaq/include/crow/query_string.h +-- Up-to-date: /usr/local/cudaq/include/crow/logging.h +-- Up-to-date: /usr/local/cudaq/include/crow/compression.h +-- Up-to-date: /usr/local/cudaq/include/crow/http_request.h +-- Up-to-date: /usr/local/cudaq/include/crow/app.h +-- Up-to-date: /usr/local/cudaq/include/crow/TinySHA1.hpp +-- Up-to-date: /usr/local/cudaq/include/crow/socket_adaptors.h +-- Up-to-date: /usr/local/cudaq/include/crow/http_response.h +-- Up-to-date: /usr/local/cudaq/include/crow/parser.h +-- Up-to-date: /usr/local/cudaq/include/crow/task_timer.h +-- Up-to-date: /usr/local/cudaq/include/crow/settings.h +-- Up-to-date: /usr/local/cudaq/include/crow/http_server.h +-- Up-to-date: /usr/local/cudaq/include/crow/returnable.h +-- Up-to-date: /usr/local/cudaq/include/crow/middleware.h +-- Up-to-date: /usr/local/cudaq/include/crow/routing.h +-- Up-to-date: /usr/local/cudaq/include/crow/common.h +-- Up-to-date: /usr/local/cudaq/include/crow/ci_map.h +-- Up-to-date: /usr/local/cudaq/include/crow/websocket.h +-- Up-to-date: /usr/local/cudaq/include/crow/json.h +-- Up-to-date: /usr/local/cudaq/include/crow/version.h +-- Up-to-date: /usr/local/cudaq/include/crow/http_parser_merged.h +-- Up-to-date: /usr/local/cudaq/include/crow/multipart.h +-- Up-to-date: /usr/local/cudaq/include/crow/mustache.h +-- Up-to-date: /usr/local/cudaq/include/crow/mime_types.h +-- Up-to-date: /usr/local/cudaq/include/crow/middlewares +-- Up-to-date: /usr/local/cudaq/include/crow/middlewares/cookie_parser.h +-- Up-to-date: /usr/local/cudaq/include/crow/middlewares/session.h +-- Up-to-date: /usr/local/cudaq/include/crow/middlewares/cors.h +-- Up-to-date: /usr/local/cudaq/include/crow/middlewares/utf-8.h +-- Up-to-date: /usr/local/cudaq/include/crow/http_connection.h +-- Up-to-date: /usr/local/cudaq/include/crow/middleware_context.h +-- Up-to-date: /usr/local/cudaq/include/crow/utility.h +-- Up-to-date: /usr/local/cudaq/include/crow.h +-- Up-to-date: /usr/local/cudaq/lib/cmake/Crow/CrowTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/Crow/Findasio.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/Crow/CrowConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQCommonConfig.cmake +-- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmDefaultConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQNloptConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQSpinConfig.cmake +-- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQOperatorConfig.cmake +-- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEnsmallenConfig.cmake +-- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPlatformDefaultConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPythonInteropConfig.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeCUDAQCompiler.cmake.in +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeCUDAQInformation.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeDetermineCUDAQCompiler.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeTestCUDAQCompiler.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/nvqir/NVQIRConfig.cmake +-- Installing: /usr/local/cudaq/lib/libcudaq-mlirgen.a +-- Installing: /usr/local/cudaq/lib/libOptimBuilder.a +-- Installing: /usr/local/cudaq/lib/libOptCodeGen.a +-- Up-to-date: /usr/local/cudaq/lib/libOptCodeGen.a +-- Installing: /usr/local/cudaq/lib/libCCDialect.a +-- Up-to-date: /usr/local/cudaq/lib/libCCDialect.a +-- Installing: /usr/local/cudaq/lib/libQuakeDialect.a +-- Up-to-date: /usr/local/cudaq/lib/libQuakeDialect.a +-- Installing: /usr/local/cudaq/lib/libOptTransforms.a +-- Up-to-date: /usr/local/cudaq/lib/libOptTransforms.a +-- Up-to-date: /usr/local/cudaq/lib/libCUDAQuantumMLIRCAPI.a +-- Up-to-date: /usr/local/cudaq/lib/objects-Release/obj.CUDAQuantumMLIRCAPI/Dialects.cpp.o +-- Up-to-date: /usr/local/cudaq/lib/libCUDAQSupport.a +-- Up-to-date: /usr/local/cudaq/lib/libCUDAQTargetConfigUtil.a +-- Installing: /usr/local/cudaq/lib/libcudaq-common.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQCommonTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQCommonTargets-release.cmake +-- Up-to-date: /usr/local/cudaq/./cacert.pem +-- Installing: /usr/local/cudaq/lib/libcudaq-mlir-runtime.so +-- Installing: /usr/local/cudaq/lib/libnvqir.so +-- Installing: /usr/local/cudaq/lib/libnvqir-qpp.so +-- Installing: /usr/local/cudaq/lib/libnvqir-dm.so +-- Up-to-date: /usr/local/cudaq/targets/qpp-cpu.yml +-- Up-to-date: /usr/local/cudaq/targets/density-matrix-cpu.yml +-- Installing: /usr/local/cudaq/lib/libnvqir-stim.so +-- Up-to-date: /usr/local/cudaq/targets/stim.yml +-- Up-to-date: /usr/local/cudaq/lib/libnvqir-custatevec-kernels.so +-- Installing: /usr/local/cudaq/lib/libnvqir-custatevec-fp64.so +-- Installing: /usr/local/cudaq/lib/libnvqir-custatevec-fp32.so +-- Up-to-date: /usr/local/cudaq/include/nvqir/CuStateVecCircuitSimulator.h +-- Installing: /usr/local/cudaq/lib/libnvqir-tensornet.so +-- Up-to-date: /usr/local/cudaq/targets/tensornet.yml +-- Installing: /usr/local/cudaq/lib/libnvqir-tensornet-mps.so +-- Up-to-date: /usr/local/cudaq/targets/tensornet-mps.yml +-- Installing: /usr/local/cudaq/lib/libnvqir-dynamics.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/nvqir/NVQIRTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/nvqir/NVQIRTargets-release.cmake +-- Up-to-date: /usr/local/cudaq/lib/libcudaq-spin.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQSpinTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQSpinTargets-release.cmake +-- Installing: /usr/local/cudaq/lib/libcudaq-em-default.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmDefaultTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmDefaultTargets-release.cmake +-- Installing: /usr/local/cudaq/lib/libcudaq-em-photonics.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmPhotonicsTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmPhotonicsTargets-release.cmake +-- Up-to-date: /usr/local/cudaq/targets/orca-photonics.yml +-- Up-to-date: /usr/local/cudaq/lib/libcudaq-nlopt.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQNloptTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQNloptTargets-release.cmake +-- Up-to-date: /usr/local/cudaq/lib/libcudaq-ensmallen.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEnsmallenTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEnsmallenTargets-release.cmake +-- Installing: /usr/local/cudaq/lib/libcudaq-platform-default.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPlatformDefaultTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPlatformDefaultTargets-release.cmake +-- Installing: /usr/local/cudaq/targets/anyon.yml +-- Up-to-date: /usr/local/cudaq/targets/mapping/anyon/berkeley-25q.txt +-- Up-to-date: /usr/local/cudaq/targets/mapping/anyon/telegraph-8q.txt +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-anyon.so +-- Installing: /usr/local/cudaq/targets/infleqtion.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-infleqtion.so +-- Installing: /usr/local/cudaq/targets/ionq.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-ionq.so +-- Installing: /usr/local/cudaq/targets/iqm.yml +-- Up-to-date: /usr/local/cudaq/targets/mapping/iqm/Adonis.txt +-- Up-to-date: /usr/local/cudaq/targets/mapping/iqm/Apollo.txt +-- Up-to-date: /usr/local/cudaq/targets/mapping/iqm/Aphrodite.txt +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-iqm.so +-- Installing: /usr/local/cudaq/targets/oqc.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-oqc.so +-- Up-to-date: /usr/local/cudaq/targets/mapping/oqc/lucy.txt +-- Up-to-date: /usr/local/cudaq/targets/mapping/oqc/toshiko.txt +-- Installing: /usr/local/cudaq/targets/quantinuum.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-quantinuum.so +-- Installing: /usr/local/cudaq/targets/braket.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-braket.so +-- Installing: /usr/local/cudaq/lib/libcudaq-rest-qpu.so +-- Installing: /usr/local/cudaq/lib/librest-remote-platform-client.so +-- Installing: /usr/local/cudaq/lib/librest-remote-platform-server.so +-- Up-to-date: /usr/local/cudaq/targets/opt-test.yml +-- Up-to-date: /usr/local/cudaq/targets/nvidia.yml +-- Up-to-date: /usr/local/cudaq/targets/nvidia-fp64.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-orca-qpu.so +-- Up-to-date: /usr/local/cudaq/targets/orca.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-remote-simulator-qpu.so +-- Up-to-date: /usr/local/cudaq/targets/nvidia-mqpu.yml +-- Up-to-date: /usr/local/cudaq/targets/nvidia-mqpu-mps.yml +-- Up-to-date: /usr/local/cudaq/targets/nvidia-mqpu-fp64.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-platform-mqpu.so +-- Up-to-date: /usr/local/cudaq/targets/remote-mqpu.yml +-- Up-to-date: /usr/local/cudaq/targets/nvqc.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-fermioniq-qpu.so +-- Installing: /usr/local/cudaq/targets/fermioniq.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-fermioniq.so +-- Installing: /usr/local/cudaq/lib/libcudaq-quera-qpu.so +-- Installing: /usr/local/cudaq/targets/quera.yml +-- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-quera.so +-- Installing: /usr/local/cudaq/lib/libcudaq-builder.so +-- Up-to-date: /usr/local/cudaq/lib/libcudaq-chemistry.so +-- Installing: /usr/local/cudaq/lib/libcudaq-operator.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQOperatorTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQOperatorTargets-release.cmake +-- Installing: /usr/local/cudaq/lib/libcudaq.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQTargets-release.cmake +-- Installing: /usr/local/cudaq/distributed_interfaces/distributed_capi.h +-- Installing: /usr/local/cudaq/distributed_interfaces/mpi_comm_impl.cpp +-- Up-to-date: /usr/local/cudaq/distributed_interfaces/activate_custom_mpi.sh +-- Up-to-date: /usr/local/cudaq/include/cudaq +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/remote +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/custatevec +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/helpers +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/helpers/MQPUUtils.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/fermioniq +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/fermioniq/FermioniqBaseQPU.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/fermioniq/helpers +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/qpu.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/orca_qpu.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/OrcaRemoteRESTQPU.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/OrcaExecutor.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/OrcaServerHelper.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/quantum_platform.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/common +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server/helpers +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server/helpers/server_impl +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server/helpers/server_impl/RestServer.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/quantinuum +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/anyon +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/oqc +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/braket +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/ionq +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/iqm +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/infleqtion +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/QuantumExecutionQueue.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/quera +-- Installing: /usr/local/cudaq/include/cudaq/platform/quera/QuEraBaseQPU.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform/quera/QuEraServerHelper.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/Todo.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/dynamics +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/helpers.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/manipulation.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/boson_operators.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/operator_leafs.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/matrix_operators.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/spin_operators.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/callback.h +-- Installing: /usr/local/cudaq/include/cudaq/dynamics/templates.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/orca.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/uccsd.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/MoleculePackageDriver.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/molecule.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/hwe.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/platform.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/host_config.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis +-- Installing: /usr/local/cudaq/include/cudaq/qis/pauli_word.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qudit.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qspan.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qreg.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qview.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qubit_qis.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/default +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/BasicExecutionManager.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/photonics +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/photonics/photonics_qis.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qvector.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qkernel.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/modifiers.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/execution_manager.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/remote_state.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qarray.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/qis/state.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/distributed +-- Up-to-date: /usr/local/cudaq/include/cudaq/distributed/builtin +-- Installing: /usr/local/cudaq/include/cudaq/distributed/distributed_capi.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/distributed/mpi_plugin.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/gradients.h +-- Installing: /usr/local/cudaq/include/cudaq/schedule.h +-- Installing: /usr/local/cudaq/include/cudaq/base_integrator.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/matrix.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/spin +-- Up-to-date: /usr/local/cudaq/include/cudaq/kernels +-- Up-to-date: /usr/local/cudaq/include/cudaq/kernels/givens_rotation.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/kernels/fermionic_swap.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/builder.h +-- Installing: /usr/local/cudaq/include/cudaq/operators.h +-- Installing: /usr/local/cudaq/include/cudaq/base_time_stepper.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/optimizers.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/photonics.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/simulators.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/utils +-- Up-to-date: /usr/local/cudaq/include/cudaq/utils/cudaq_utils.h +-- Installing: /usr/local/cudaq/include/cudaq/utils/tensor.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/utils/registry.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/remote_capabilities.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradient.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/vqe.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/nlopt +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/nlopt/nlopt.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/ensmallen +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/ensmallen/ensmallen.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/sample.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizer.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/draw.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/broadcast.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/get_state.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/evolve.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients/forward_difference.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients/central_difference.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients/parameter_shift.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/resource_estimation.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/observe.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/state.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/spin_op.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/algorithm.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/builder +-- Up-to-date: /usr/local/cudaq/include/cudaq/builder/kernels.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/builder/QuakeValue.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/builder/kernel_builder.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/target_control.h +-- Up-to-date: /usr/local/cudaq/include/cudaq/concepts.h +-- Up-to-date: /usr/local/cudaq/include/common +-- Installing: /usr/local/cudaq/include/common/NoiseModel.h +-- Up-to-date: /usr/local/cudaq/include/common/EigenSparse.h +-- Up-to-date: /usr/local/cudaq/include/common/JIT.h +-- Up-to-date: /usr/local/cudaq/include/common/JsonConvert.h +-- Up-to-date: /usr/local/cudaq/include/common/BaseRemoteSimulatorQPU.h +-- Up-to-date: /usr/local/cudaq/include/common/NvqcConfig.h +-- Installing: /usr/local/cudaq/include/common/BaseRemoteRESTQPU.h +-- Up-to-date: /usr/local/cudaq/include/common/MeasureCounts.h +-- Up-to-date: /usr/local/cudaq/include/common/Executor.h +-- Up-to-date: /usr/local/cudaq/include/common/CustomOp.h +-- Up-to-date: /usr/local/cudaq/include/common/ServerHelper.h +-- Up-to-date: /usr/local/cudaq/include/common/Logger.h +-- Up-to-date: /usr/local/cudaq/include/common/ObserveResult.h +-- Up-to-date: /usr/local/cudaq/include/common/ThunkInterface.h +-- Installing: /usr/local/cudaq/include/common/BraketExecutor.h +-- Up-to-date: /usr/local/cudaq/include/common/ArgumentWrapper.h +-- Up-to-date: /usr/local/cudaq/include/common/ArgumentConversion.h +-- Up-to-date: /usr/local/cudaq/include/common/Trace.h +-- Up-to-date: /usr/local/cudaq/include/common/QuditIdTracker.h +-- Up-to-date: /usr/local/cudaq/include/common/BaseRestRemoteClient.h +-- Up-to-date: /usr/local/cudaq/include/common/EigenDense.h +-- Up-to-date: /usr/local/cudaq/include/common/FmtCore.h +-- Up-to-date: /usr/local/cudaq/include/common/PluginUtils.h +-- Up-to-date: /usr/local/cudaq/include/common/EvolveResult.h +-- Up-to-date: /usr/local/cudaq/include/common/Future.h +-- Up-to-date: /usr/local/cudaq/include/common/RemoteKernelExecutor.h +-- Installing: /usr/local/cudaq/include/common/RuntimeMLIRCommonImpl.h +-- Up-to-date: /usr/local/cudaq/include/common/GPUInfo.h +-- Up-to-date: /usr/local/cudaq/include/common/RuntimeMLIR.h +-- Up-to-date: /usr/local/cudaq/include/common/Registry.h +-- Up-to-date: /usr/local/cudaq/include/common/Resources.h +-- Up-to-date: /usr/local/cudaq/include/common/KernelWrapper.h +-- Up-to-date: /usr/local/cudaq/include/common/AnalogHamiltonian.h +-- Up-to-date: /usr/local/cudaq/include/common/SerializedCodeExecutionContext.h +-- Up-to-date: /usr/local/cudaq/include/common/Environment.h +-- Installing: /usr/local/cudaq/include/common/BraketServerHelper.h +-- Up-to-date: /usr/local/cudaq/include/common/UnzipUtils.h +-- Up-to-date: /usr/local/cudaq/include/common/ExecutionContext.h +-- Up-to-date: /usr/local/cudaq/include/common/SimulationState.h +-- Up-to-date: /usr/local/cudaq/include/common/RestClient.h +-- Up-to-date: /usr/local/cudaq/include/common/Timing.h +-- Up-to-date: /usr/local/cudaq/include/nvqir/CircuitSimulator.h +-- Up-to-date: /usr/local/cudaq/include/nvqir/QIRTypes.h +-- Up-to-date: /usr/local/cudaq/include/nvqir/Gates.h +-- Installing: /usr/local/cudaq/include/cudaq.h +-- Installing: /usr/local/cudaq/bin/cudaq-opt +-- Installing: /usr/local/cudaq/bin/cudaq-translate +-- Installing: /usr/local/cudaq/bin/cudaq-lsp-server +-- Installing: /usr/local/cudaq/bin/cudaq-quake +-- Up-to-date: /usr/local/cudaq/bin/fixup-linkage +-- Up-to-date: /usr/local/cudaq/include/nvqpp/nvqpp_config.h +-- Installing: /usr/local/cudaq/bin/nvq++ +-- Up-to-date: /usr/local/cudaq/targets/backendConfig.cpp +-- Up-to-date: /usr/local/cudaq/bin/cudaq-target-conf +-- Installing: /usr/local/cudaq/bin/cudaq-qpud +-- Up-to-date: /usr/local/cudaq/bin/cudaq-qpud.py +-- Up-to-date: /usr/local/cudaq/bin/nvqc_proxy.py +-- Up-to-date: /usr/local/cudaq/bin/json_request_runner.py +-- Installing: /usr/local/cudaq/lib/libcudaq-py-utils.so +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.quake/dialects/quake.py +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.quake.ops_gen/dialects/_quake_ops_gen.py +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.cc/dialects/cc.py +-- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.cc.ops_gen/dialects/_cc_ops_gen.py +-- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/CUDAQuantumExtension.cpp +-- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_ExecutionContext.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_NoiseModel.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_EvolveResult.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_ObserveResult.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_SampleResult.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_CustomOpRegistry.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_AnalogHamiltonian.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_draw.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_observe_async.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_optimizer.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_sample_async.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_state.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_evolve.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_translate.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_utils.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_vqe.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/platform/JITExecutionCache.cpp +-- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/platform/py_alt_launch_kernel.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/qis/py_execution_manager.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/qis/py_qubit_qis.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/spin/py_matrix.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/spin/py_spin_op.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/target/py_runtime_target.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/target/py_testing_utils.cpp +-- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/mlir/py_register_dialects.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyRemoteRESTQPU.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyFermioniqRESTQPU.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyQuEraRemoteRESTQPU.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyRemoteSimulatorQPU.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyRestRemoteClient.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../utils/LinkedLibraryHolder.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/common/ArgumentConversion.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/common/QuantumExecutionQueue.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/default/rest_server/RemoteRuntimeClient.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaExecutor.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaQPU.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaRemoteRESTQPU.cpp +-- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaServerHelper.cpp +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/libCUDAQuantumPythonCAPI.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/libCUDAQuantumPythonCAPI.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/quake.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_quake_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/cc.py +-- Installing: /usr/local/cudaq/cudaq/mlir/dialects/_cc_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_quakeDialects.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_quakeDialects.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN:$ORIGIN/../../../lib:$ORIGIN/../../../lib/plugins" +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirRegisterEverything.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirRegisterEverything.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/async_dialect/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/async_dialect/passes/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_async_dialect_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirAsyncPasses.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirAsyncPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/bufferization.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_bufferization_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_bufferization_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/builtin.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_builtin_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_builtin_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/complex.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_complex_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/cf.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_cf_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/func.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_func_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_func_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/gpu/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/gpu/passes/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_gpu_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirGPUPasses.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirGPUPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_linalg_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/dump_oplib.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/affine.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/comprehension.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/config.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/dsl.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/emitter.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/scalar_expr.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/types.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/yaml_helper.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/ops/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/ops/core_named_ops.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/passes/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_linalg_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsLinalg.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsLinalg.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirLinalgPasses.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirLinalgPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_transform_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/transform/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/dialects/transform/__init__.pyi +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_transform_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsTransform.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsTransform.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_loop_transform_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/transform/loop.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_loop_transform_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_structured_transform_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/transform/structured.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_structured_transform_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/math.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_math_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/arith.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_arith_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_arith_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/memref.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_memref_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_memref_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/ml_program.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_ml_program_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_ml_program_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/quant.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/dialects/quant.pyi +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsQuant.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsQuant.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/pdl.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_pdl_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/dialects/pdl.pyi +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_pdl_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsPDL.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsPDL.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/scf.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_scf_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_scf_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/shape.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_shape_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/sparse_tensor.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_sparse_tensor_ops_gen.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsSparseTensor.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsSparseTensor.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirSparseTensorPasses.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirSparseTensorPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/tensor.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_tensor_ops_ext.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_tensor_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/tosa.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_tosa_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/vector.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_vector_ops_gen.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/ir.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/passmanager.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_ods_common.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/__init__.pyi +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/ir.pyi +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/passmanager.pyi +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/execution_engine.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.pyi +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/runtime/__init__.py +-- Up-to-date: /usr/local/cudaq/cudaq/mlir/runtime/np_to_memref.py +-- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.cpython-310-x86_64-linux-gnu.so +-- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" +-- Installing: /usr/local/cudaq/lib/plugins/libcudaq-pyscf.so +-- Up-to-date: /usr/local/cudaq/./cudaq +-- Up-to-date: /usr/local/cudaq/./cudaq/display +-- Up-to-date: /usr/local/cudaq/./cudaq/display/display_trace.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator +-- Installing: /usr/local/cudaq/./cudaq/operator/integrator.py +-- Installing: /usr/local/cudaq/./cudaq/operator/helpers.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/CMakeLists.txt +-- Installing: /usr/local/cudaq/./cudaq/operator/evolution.py +-- Installing: /usr/local/cudaq/./cudaq/operator/cudm_solver.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/definitions.py +-- Installing: /usr/local/cudaq/./cudaq/operator/manipulation.py +-- Installing: /usr/local/cudaq/./cudaq/operator/cudm_state.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators +-- Installing: /usr/local/cudaq/./cudaq/operator/integrators/builtin_integrators.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators/cuda_torchdiffeq_integrator.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators/scipy_integrators.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/expressions.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/dynamics.yml +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/cudm_helpers.py +-- Installing: /usr/local/cudaq/./cudaq/operator/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/operator/schedule.py +-- Up-to-date: /usr/local/cudaq/./cudaq/domains +-- Up-to-date: /usr/local/cudaq/./cudaq/domains/chemistry +-- Up-to-date: /usr/local/cudaq/./cudaq/domains/chemistry/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/domains/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/qis +-- Up-to-date: /usr/local/cudaq/./cudaq/qis/qis.py +-- Up-to-date: /usr/local/cudaq/./cudaq/qis/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernels +-- Up-to-date: /usr/local/cudaq/./cudaq/kernels/hwe.py +-- Installing: /usr/local/cudaq/./cudaq/kernels/uccsd.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernels/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/lib +-- Up-to-date: /usr/local/cudaq/./cudaq/lib/composite_operations +-- Up-to-date: /usr/local/cudaq/./cudaq/lib/composite_operations/fswap.py +-- Up-to-date: /usr/local/cudaq/./cudaq/lib/composite_operations/givens.py +-- Up-to-date: /usr/local/cudaq/./cudaq/lib/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/util +-- Up-to-date: /usr/local/cudaq/./cudaq/util/timing_helper.py +-- Up-to-date: /usr/local/cudaq/./cudaq/handlers +-- Up-to-date: /usr/local/cudaq/./cudaq/handlers/photonics_kernel.py +-- Up-to-date: /usr/local/cudaq/./cudaq/handlers/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/visualization +-- Up-to-date: /usr/local/cudaq/./cudaq/visualization/bloch_visualize.py +-- Up-to-date: /usr/local/cudaq/./cudaq/visualization/bloch_visualize_err.py +-- Up-to-date: /usr/local/cudaq/./cudaq/mlir +-- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects +-- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/CCOps.td +-- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/QuakeOps.td +-- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/cc.py +-- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/quake.py +-- Up-to-date: /usr/local/cudaq/./cudaq/_packages.py +-- Up-to-date: /usr/local/cudaq/./cudaq/dbg +-- Up-to-date: /usr/local/cudaq/./cudaq/dbg/ast.py +-- Up-to-date: /usr/local/cudaq/./cudaq/dbg/__init__.py +-- Up-to-date: /usr/local/cudaq/./cudaq/runtime +-- Up-to-date: /usr/local/cudaq/./cudaq/runtime/utils.py +-- Up-to-date: /usr/local/cudaq/./cudaq/runtime/state.py +-- Installing: /usr/local/cudaq/./cudaq/runtime/observe.py +-- Up-to-date: /usr/local/cudaq/./cudaq/runtime/sample.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/quake_value.py +-- Installing: /usr/local/cudaq/./cudaq/kernel/ast_bridge.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/utils.py +-- Installing: /usr/local/cudaq/./cudaq/kernel/kernel_decorator.py +-- Installing: /usr/local/cudaq/./cudaq/kernel/kernel_builder.py +-- Installing: /usr/local/cudaq/./cudaq/kernel/analysis.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/register_op.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/common +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/common/fermionic_swap.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/common/givens.py +-- Up-to-date: /usr/local/cudaq/./cudaq/kernel/captured_data.py +-- Up-to-date: /usr/local/cudaq/./cudaq/__init__.py +-- Installing: /usr/local/cudaq/cudaq/_metadata.py +-- Up-to-date: /usr/local/cudaq/include/cudaq/python/PythonCppInterop.h +-- Installing: /usr/local/cudaq/lib/libcudaq-python-interop.so +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPythonInteropTargets.cmake +-- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPythonInteropTargets-release.cmake +-- Up-to-date: /usr/local/cudaq/targets/dynamics.yml +-- Up-to-date: /usr/local/cudaq/lib/CustomPassPlugin.so +-- Installing: /usr/local/cudaq/lib/libcudaq-em-qudit.so +-- Set runtime path of "/usr/local/cudaq/lib/libcudaq-em-qudit.so" to "$ORIGIN:$ORIGIN/lib:$ORIGIN/lib/plugins:$ORIGIN/../lib:$ORIGIN/../lib/plugins" +Installed CUDA-Q in directory: /usr/local/cudaq diff --git a/python/cudaq/operator/manipulation.py b/python/cudaq/operator/manipulation.py index a54a154049..dc6d31c819 100644 --- a/python/cudaq/operator/manipulation.py +++ b/python/cudaq/operator/manipulation.py @@ -135,30 +135,6 @@ def _canonicalize( _OperatorHelpers.permute_matrix(op_matrix, permutation) return op_matrix, canon_degrees - def _canonicalize(self: MatrixArithmetics, op_matrix: NDArray[numpy.complexfloating], op_degrees: Iterable[int]) -> tuple[NDArray[numpy.complexfloating], tuple[int]]: - """ - Given a matrix representation that acts on the given degrees or freedom, - sorts the degrees and permutes the matrix to match that canonical order. - - Returns: - A tuple consisting of the permuted matrix as well as the sequence of degrees - of freedom in canonical order. - """ - canon_degrees = _OperatorHelpers.canonicalize_degrees(op_degrees) - if op_degrees != canon_degrees: - # There may be a more efficient way, but I needed something correct first. - states = _OperatorHelpers.generate_all_states(canon_degrees, self._dimensions) - indices = dict([(d, idx) for idx, d in enumerate(canon_degrees)]) - reordering = [indices[op_degree] for op_degree in op_degrees] - # [degrees[i] for i in reordering] produces op_degrees - op_states = _OperatorHelpers.generate_all_states(op_degrees, self._dimensions) - state_indices = dict([(state, idx) for idx, state in enumerate(states)]) - permutation = [state_indices[op_state] for op_state in op_states] - # [states[i] for i in permutation] produces op_states - _OperatorHelpers.permute_matrix(op_matrix, permutation) - return op_matrix, canon_degrees - return op_matrix, canon_degrees - def tensor(self: MatrixArithmetics, op1: MatrixArithmetics.Evaluated, op2: MatrixArithmetics.Evaluated) -> MatrixArithmetics.Evaluated: """ diff --git a/v1.perf b/v1.perf deleted file mode 100644 index 1de34b75b3..0000000000 --- a/v1.perf +++ /dev/null @@ -1,104 +0,0 @@ - -multiplication inplace with itself -nr ops: 100 -New setup took 2.8217e-05 seconds. -nr ops: 1000 -New setup took 0.000122433 seconds. -nr ops: 10000 -New setup took 0.0011764 seconds. - -multiplication inplace with other -nr ops: 100 -New setup took 5.8016e-05 seconds. -nr ops: 1000 -New setup took 0.00074998 seconds. -nr ops: 10000 -New setup took 0.112577 seconds. - -multiplication inplace with other (reverse order) -nr ops: 100 -New setup took 2.5015e-05 seconds. -nr ops: 1000 -New setup took 0.000481467 seconds. -nr ops: 10000 -New setup took 0.0743091 seconds. - -addition inplace with itself -nr ops: 100 -New setup took 3.7362e-05 seconds. -nr ops: 1000 -New setup took 0.000141091 seconds. -nr ops: 10000 -New setup took 0.00151542 seconds. - -addition inplace with other -nr ops: 100 -New setup took 4.2818e-05 seconds. -nr ops: 1000 -New setup took 0.000304375 seconds. -nr ops: 10000 -New setup took 0.00325976 seconds. - -addition inplace with other (reverse order) -nr ops: 100 -New setup took 3.1124e-05 seconds. -nr ops: 1000 -New setup took 0.000248507 seconds. -nr ops: 10000 -New setup took 0.00285919 seconds. - -addition inplace with product self -nr ops: 100 -New setup took 0.00024566 seconds. -nr ops: 1000 -New setup took 0.00236887 seconds. -nr ops: 10000 -New setup took 0.0230741 seconds. - -addition inplace with product self (reverse order) -nr ops: 100 -New setup took 0.000246139 seconds. -nr ops: 1000 -New setup took 0.00232941 seconds. -nr ops: 10000 -New setup took 0.0231446 seconds. - -product inplace with 2-term sum on fixed degrees -nr ops: 100 -New setup took 0.000190887 seconds. -nr ops: 1000 -New setup took 0.00181602 seconds. -nr ops: 10000 -New setup took 0.0201958 seconds. - -product inplace with 2-term sum on varying degrees -nr ops: 10 -New setup took 0.00175238 seconds. -nr ops: 20 -New setup took 3.00176 seconds. - -product inplace with 2-term sum on varying degrees (reverse order) -nr ops: 10 -New setup took 0.0012573 seconds. -nr ops: 20 -New setup took 2.47935 seconds. - -sum of products of random terms -nr terms 10, term length 100 -New setup took 0.000131233 seconds. -nr terms 10, term length 1000 -New setup took 0.000725484 seconds. -nr terms 10, term length 10000 -New setup took 0.00554068 seconds. -nr terms 100, term length 100 -New setup took 0.00098111 seconds. -nr terms 100, term length 1000 -New setup took 0.00613552 seconds. -nr terms 100, term length 10000 -New setup took 0.0517298 seconds. -nr terms 1000, term length 100 -New setup took 0.0115695 seconds. -nr terms 1000, term length 1000 -New setup took 0.062983 seconds. -nr terms 1000, term length 10000 -New setup took 0.503548 seconds. diff --git a/v2.perf b/v2.perf deleted file mode 100644 index 3bb78ca93b..0000000000 --- a/v2.perf +++ /dev/null @@ -1,108 +0,0 @@ - -multiplication inplace with itself -nr ops: 100 -New setup took 6.1922e-05 seconds. -nr ops: 1000 -New setup took 0.000226954 seconds. -nr ops: 10000 -New setup took 0.00221656 seconds. - -multiplication inplace with other -nr ops: 100 -New setup took 0.000310864 seconds. -nr ops: 1000 -New setup took 0.0201444 seconds. -nr ops: 10000 -New setup took 2.35453 seconds. - -multiplication inplace with other (reverse order) -nr ops: 100 -New setup took 9.8062e-05 seconds. -nr ops: 1000 -New setup took 0.00733987 seconds. -nr ops: 10000 -New setup took 0.787652 seconds. - -addition inplace with itself -nr ops: 100 -New setup took 3.5232e-05 seconds. -nr ops: 1000 -New setup took 0.00020187 seconds. -nr ops: 10000 -New setup took 0.00228643 seconds. - -addition inplace with other -nr ops: 100 -New setup took 6.3769e-05 seconds. -nr ops: 1000 -New setup took 0.000345559 seconds. -nr ops: 10000 -New setup took 0.00422566 seconds. - -addition inplace with other (reverse order) -nr ops: 100 -New setup took 0.00023781 seconds. -nr ops: 1000 -New setup took 0.000287685 seconds. -nr ops: 10000 -New setup took 0.00293226 seconds. - -addition inplace with product self -nr ops: 100 -New setup took 0.000141212 seconds. -nr ops: 1000 -New setup took 0.0012408 seconds. -nr ops: 10000 -New setup took 0.0103075 seconds. - -addition inplace with product self (reverse order) -nr ops: 100 -New setup took 0.000147012 seconds. -nr ops: 1000 -New setup took 0.00126523 seconds. -nr ops: 10000 -New setup took 0.0106776 seconds. - -product inplace with 2-term sum on fixed degrees -nr ops: 100 -New setup took 0.000316067 seconds. -nr ops: 1000 -New setup took 0.00306611 seconds. -nr ops: 10000 -New setup took 0.0314979 seconds. - -product inplace with 2-term sum on varying degrees -nr ops: 10 -New setup took 0.0025696 seconds. -nr ops: 20 -New setup took 6.56947 seconds. -nr ops: 30 -New setup is killed (out of memory?) - skipped - -product inplace with 2-term sum on varying degrees (reverse order) -nr ops: 10 -New setup took 0.00195102 seconds. -nr ops: 20 -New setup took 4.47496 seconds. -nr ops: 30 -New setup is killed (out of memory?) - skipped - -sum of products of random terms -nr terms 10, term length 100 -New setup took 0.00114159 seconds. -nr terms 10, term length 1000 -New setup took 0.0144861 seconds. -nr terms 10, term length 10000 -New setup took 0.15289 seconds. -nr terms 100, term length 100 -New setup took 0.0102112 seconds. -nr terms 100, term length 1000 -New setup took 0.167844 seconds. -nr terms 100, term length 10000 -New setup took 1.50191 seconds. -nr terms 1000, term length 100 -New setup took 0.0975547 seconds. -nr terms 1000, term length 1000 -New setup took 1.50753 seconds. -nr terms 1000, term length 10000 -New setup took 15.371 seconds. diff --git a/v3.perf b/v3.perf deleted file mode 100644 index 27bba8ec26..0000000000 --- a/v3.perf +++ /dev/null @@ -1,104 +0,0 @@ - -multiplication inplace with itself -nr ops: 100 -New setup took 2.6647e-05 seconds. -nr ops: 1000 -New setup took 0.000107774 seconds. -nr ops: 10000 -New setup took 0.00142158 seconds. - -multiplication inplace with other -nr ops: 100 -New setup took 4.9103e-05 seconds. -nr ops: 1000 -New setup took 0.000718737 seconds. -nr ops: 10000 -New setup took 0.122428 seconds. - -multiplication inplace with other (reverse order) -nr ops: 100 -New setup took 2.1195e-05 seconds. -nr ops: 1000 -New setup took 0.000388353 seconds. -nr ops: 10000 -New setup took 0.0703014 seconds. - -addition inplace with itself -nr ops: 100 -New setup took 3.8241e-05 seconds. -nr ops: 1000 -New setup took 0.000111467 seconds. -nr ops: 10000 -New setup took 0.00124563 seconds. - -addition inplace with other -nr ops: 100 -New setup took 4.8936e-05 seconds. -nr ops: 1000 -New setup took 0.000299443 seconds. -nr ops: 10000 -New setup took 0.00407947 seconds. - -addition inplace with other (reverse order) -nr ops: 100 -New setup took 7.1686e-05 seconds. -nr ops: 1000 -New setup took 0.000210669 seconds. -nr ops: 10000 -New setup took 0.00241548 seconds. - -addition inplace with product self -nr ops: 100 -New setup took 0.000253843 seconds. -nr ops: 1000 -New setup took 0.00227643 seconds. -nr ops: 10000 -New setup took 0.0242446 seconds. - -addition inplace with product self (reverse order) -nr ops: 100 -New setup took 0.00023314 seconds. -nr ops: 1000 -New setup took 0.00234443 seconds. -nr ops: 10000 -New setup took 0.0233708 seconds. - -product inplace with 2-term sum on fixed degrees -nr ops: 100 -New setup took 0.000148147 seconds. -nr ops: 1000 -New setup took 0.00119577 seconds. -nr ops: 10000 -New setup took 0.0119991 seconds. - -product inplace with 2-term sum on varying degrees -nr ops: 10 -New setup took 0.000662715 seconds. -nr ops: 20 -New setup took 2.84599 seconds. - -product inplace with 2-term sum on varying degrees (reverse order) -nr ops: 10 -New setup took 0.00101627 seconds. -nr ops: 20 -New setup took 2.26694 seconds. - -sum of products of random terms -nr terms 10, term length 100 -New setup took 0.000133817 seconds. -nr terms 10, term length 1000 -New setup took 0.000672323 seconds. -nr terms 10, term length 10000 -New setup took 0.00515006 seconds. -nr terms 100, term length 100 -New setup took 0.00111038 seconds. -nr terms 100, term length 1000 -New setup took 0.0058762 seconds. -nr terms 100, term length 10000 -New setup took 0.0498336 seconds. -nr terms 1000, term length 100 -New setup took 0.00966492 seconds. -nr terms 1000, term length 1000 -New setup took 0.0642393 seconds. -nr terms 1000, term length 10000 -New setup took 0.494563 seconds. From 3695e7b938a2d312748c1caad3cfd87d96c5c90c Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 18 Feb 2025 11:59:09 +0000 Subject: [PATCH 301/311] forgot one file Signed-off-by: Bettina Heim --- old.perf | 104 ------------------------------------------------------- 1 file changed, 104 deletions(-) delete mode 100644 old.perf diff --git a/old.perf b/old.perf deleted file mode 100644 index 4f2eca95d3..0000000000 --- a/old.perf +++ /dev/null @@ -1,104 +0,0 @@ - -multiplication inplace with itself -nr ops: 100 -Old setup took 0.000145484 seconds. -nr ops: 1000 -Old setup took 0.0010659 seconds. -nr ops: 10000 -Old setup took 0.00943221 seconds. - -multiplication inplace with other -nr ops: 100 -Old setup took 0.000267917 seconds. -nr ops: 1000 -Old setup took 0.00509327 seconds. -nr ops: 10000 -Old setup took 0.402641 seconds. - -multiplication inplace with other (reverse order) -nr ops: 100 -Old setup took 0.000178547 seconds. -nr ops: 1000 -Old setup took 0.00640537 seconds. -nr ops: 10000 -Old setup took 0.558541 seconds. - -addition inplace with itself -nr ops: 100 -Old setup took 7.7252e-05 seconds. -nr ops: 1000 -Old setup took 0.00067837 seconds. -nr ops: 10000 -Old setup took 0.00674406 seconds. - -addition inplace with other -nr ops: 100 -Old setup took 0.00211015 seconds. -nr ops: 1000 -Old setup took 0.765903 seconds. -nr ops: 10000 -Old setup takes minutes - skipped - -addition inplace with other (reverse order) -nr ops: 100 -Old setup took 0.000765763 seconds. -nr ops: 1000 -Old setup took 0.526556 seconds. -nr ops: 10000 -Old setup takes minutes - skipped - -addition inplace with product self -nr ops: 100 -Old setup took 8.0988e-05 seconds. -nr ops: 1000 -Old setup took 0.000713935 seconds. -nr ops: 10000 -Old setup took 0.00660016 seconds. - -addition inplace with product self (reverse order) -nr ops: 100 -Old setup took 7.1369e-05 seconds. -nr ops: 1000 -Old setup took 0.000466354 seconds. -nr ops: 10000 -Old setup took 0.00452788 seconds. - -product inplace with 2-term sum on fixed degrees -nr ops: 100 -Old setup took 0.000101042 seconds. -nr ops: 1000 -Old setup took 0.000903594 seconds. -nr ops: 10000 -Old setup took 0.0091517 seconds. - -product inplace with 2-term sum on varying degrees -nr ops: 10 -Old setup took 0.0103578 seconds. -nr ops: 20 -Old setup took 1.60473 seconds. - -product inplace with 2-term sum on varying degrees (reverse order) -nr ops: 10 -Old setup segfaults -nr ops: 20 -Old setup segfaults - -sum of products of random terms -nr terms 10, term length 100 -Old setup took 0.00225446 seconds. -nr terms 10, term length 1000 -Old setup took 0.0125959 seconds. -nr terms 10, term length 10000 -Old setup took 0.121102 seconds. -nr terms 100, term length 100 -Old setup took 0.0117404 seconds. -nr terms 100, term length 1000 -Old setup took 0.116341 seconds. -nr terms 100, term length 10000 -Old setup took 1.17402 seconds. -nr terms 1000, term length 100 -Old setup took 0.12738 seconds. -nr terms 1000, term length 1000 -Old setup took 1.17435 seconds. -nr terms 1000, term length 10000 -Old setup took 11.5931 seconds. From 309b8dde4ea1d026c8dd6e6d36b9d008ea0219a2 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 18 Feb 2025 11:59:39 +0000 Subject: [PATCH 302/311] forgot one file Signed-off-by: Bettina Heim --- out.txt | 1367 ------------------------------------------------------- 1 file changed, 1367 deletions(-) delete mode 100644 out.txt diff --git a/out.txt b/out.txt deleted file mode 100644 index 2e9b041217..0000000000 --- a/out.txt +++ /dev/null @@ -1,1367 +0,0 @@ -Build directory: /workspaces/cuda-quantum/build/release -CUDA version 12.0 detected. -Using cuQuantum installation in /usr/local/lib/python3.10/dist-packages/cuquantum. -Using cuTensor installation in /usr/local/lib/python3.10/dist-packages/cutensor. -Configuring nvq++ to use the lld linker by default. -Preparing CUDA-Q build with LLVM installation in /usr/local/llvm... --- Submodule update --- CUDA Quantum static linking disabled. --- Using LLVMConfig.cmake in: /usr/local/llvm/lib/cmake/llvm --- Using ClangConfig.cmake in: /usr/local/llvm/lib/cmake/clang --- Using MLIRConfig.cmake in: /usr/local/llvm/lib/cmake/mlir --- Build type is Release --- MPI CXX Found: /usr/local/openmpi/bin/mpiexec --- Building default MPI Comm plugin --- Module support is disabled. --- Version: 8.1.2 --- Build type: Release --- CXX_STANDARD: 20 --- Required features: cxx_variadic_templates --- Build spdlog: 1.11.0 --- Build type: Release --- Using system curl for CPR. --- C++ Requests CMake Options --- ======================================================= --- CPR_GENERATE_COVERAGE: OFF --- CPR_CURL_NOSIGNAL: OFF --- CPR_USE_SYSTEM_GTEST: OFF --- CPR_FORCE_USE_SYSTEM_CURL: TRUE --- CPR_ENABLE_SSL: ON --- CPR_FORCE_OPENSSL_BACKEND: ON --- CPR_FORCE_WINSSL_BACKEND: OFF --- CPR_FORCE_DARWINSSL_BACKEND: OFF --- CPR_FORCE_MBEDTLS_BACKEND: OFF --- CPR_ENABLE_LINTING: OFF --- CPR_ENABLE_CPPCHECK: OFF --- CPR_BUILD_TESTS: OFF --- CPR_BUILD_TESTS_SSL: OFF --- ======================================================= --- Disabled SSL backend auto detect since either CPR_FORCE_OPENSSL_BACKEND, CPR_FORCE_DARWINSSL_BACKEND, CPR_FORCE_MBEDTLS_BACKEND, or CPR_FORCE_WINSSL_BACKEND is enabled. --- Using OpenSSL. --- Curl 8.5.0 found on this system. --- OpenMP Found. Adding interface definitions to target libqpp. --- Cuda language found. --- Building with -fPIC --- Building with -fPIC --- Building with -fPIC --- OpenSSL Found, building REST Client. --- OpenMP Found. Adding build flags to target nvqir-qpp: -fopenmp. --- OpenMP Found. Adding build flags to target nvqir-dm: -fopenmp. --- Found Python: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter Development Development.Module Development.Embed --- Could NOT find pybind11 (missing: pybind11_DIR) --- CUSTATEVEC_ROOT and CUDA_FOUND - building custatevec NVQIR backend. --- CUSTATEVEC_LIB: /usr/local/lib/python3.10/dist-packages/cuquantum/lib/libcustatevec.so.1 --- CUTENSOR_LIB: /usr/local/lib/python3.10/dist-packages/cutensor/lib/libcutensor.so.2 --- CUTENSORNET_LIB: /usr/local/lib/python3.10/dist-packages/cuquantum/lib/libcutensornet.so.2 --- CUTENSORNET_INC: /usr/local/lib/python3.10/dist-packages/cuquantum/include/cutensornet.h --- Found cutensornet version: 2.6.0 --- cudensitymat header: /usr/local/lib/python3.10/dist-packages/cuquantum/include/cudensitymat.h --- OpenMP Found. Adding build flags to target cudaq-spin: -fopenmp. --- Building with -fPIC --- OpenMP Found. Adding build flags to target cudaq-em-photonics: -fopenmp. --- OpenMP Found. Adding build flags to target cudaq-ensmallen: -fopenmp. --- Building with -fPIC --- Building REST QPU. --- Found AWS SDK for C++, Version: 1.11.454, Install Root:/usr/local/aws, Platform Prefix:, Platform Dependent Libraries: pthread;curl --- Components specified for AWSSDK: braket;s3-crt;sts, application will be depending on libs: aws-cpp-sdk-braket;aws-cpp-sdk-s3-crt;aws-cpp-sdk-sts;aws-cpp-sdk-core;aws-crt-cpp;aws-c-http;aws-c-mqtt;aws-c-cal;aws-c-auth;aws-c-common;aws-c-io;aws-checksums;aws-c-event-stream;aws-c-s3;aws-c-compression;aws-c-sdkutils;pthread;curl --- Try finding aws-cpp-sdk-core --- LibCrypto Include Dir: /usr/local/openssl/include --- LibCrypto Shared Lib: crypto_SHARED_LIBRARY-NOTFOUND --- LibCrypto Static Lib: crypto_STATIC_LIBRARY-NOTFOUND --- Found aws-cpp-sdk-core --- Try finding aws-cpp-sdk-sts --- Found aws-cpp-sdk-sts --- Try finding aws-cpp-sdk-s3-crt --- Found aws-cpp-sdk-s3-crt --- Try finding aws-cpp-sdk-braket --- Found aws-cpp-sdk-braket --- Found AWS SDK for C++, Version: 1.11.454, Install Root:/usr/local/aws, Platform Prefix:, Platform Dependent Libraries: pthread;curl --- Components specified for AWSSDK: braket;s3-crt;sts, application will be depending on libs: aws-cpp-sdk-braket;aws-cpp-sdk-s3-crt;aws-cpp-sdk-sts;aws-cpp-sdk-core;aws-crt-cpp;aws-c-http;aws-c-mqtt;aws-c-cal;aws-c-auth;aws-c-common;aws-c-io;aws-checksums;aws-c-event-stream;aws-c-s3;aws-c-compression;aws-c-sdkutils;pthread;curl --- Try finding aws-cpp-sdk-core --- LibCrypto Include Dir: /usr/local/openssl/include --- LibCrypto Shared Lib: crypto_SHARED_LIBRARY-NOTFOUND --- LibCrypto Static Lib: crypto_STATIC_LIBRARY-NOTFOUND --- Found aws-cpp-sdk-core --- Try finding aws-cpp-sdk-sts --- Found aws-cpp-sdk-sts --- Try finding aws-cpp-sdk-s3-crt --- Found aws-cpp-sdk-s3-crt --- Try finding aws-cpp-sdk-braket --- Found aws-cpp-sdk-braket --- Could NOT find PkgConfig (missing: PKG_CONFIG_EXECUTABLE) --- Minizip found: /usr/local/zlib/lib/libminizip.a and /usr/local/zlib/include/minizip/unzip.h --- Building ORCA REST QPU. --- Building Fermioniq REST QPU. --- Building QuEra REST QPU. --- Building with -fPIC --- OpenMP Found. Adding build flags to target cudaq-chemistry: -fopenmp. --- OpenMP Found. Adding build flags to target cudaq-operator: -fopenmp. --- Building with -fPIC --- Building with -fPIC --- Found Python: /usr/bin/python3.10 (found suitable version "3.10.12", minimum required is "3") found components: Interpreter Development Development.Module Development.Embed --- pybind11 v2.12.0 dev1 --- Building with -fPIC --- Building cudaq-pyscf plugin. --- Pytest and Numpy found, building Python tests. --- CUDAQ_TEST_MOCK_SERVERS=TRUE and dependent modules available, building Python remote QPU tests. --- Building cutensornet backend tests. --- Building with -fPIC --- Registering CustomPassPlugin as a pass plugin (static build: OFF) --- Found Python: /usr/bin/python3.10 (found version "3.10.12") found components: Interpreter --- OpenMP Found. Adding build flags to target nvqir-qpp-observe-test: -fopenmp. --- Building with -fPIC --- OpenFermion PySCF found, enabling chemistry tests. --- Configuring done (11.1s) --- Generating done (0.3s) --- Build files have been written to: /workspaces/cuda-quantum/build/release -Building CUDA-Q with configuration Release... -[1/565] Building Passes.h.inc... -[2/565] Building Passes.h.inc... -[3/565] Building CodeGenOps.cpp.inc... -[4/565] Building CodeGenOps.h.inc... -[5/565] Building CCOps.cpp.inc... -[6/565] Building CCOps.h.inc... -[7/565] Building CXX object runtime/cudaq/distributed/builtin/CMakeFiles/cudaq-comm-plugin.dir/mpi_comm_impl.cpp.o -[8/565] Linking CXX shared library lib/plugins/libcudaq-comm-plugin.so -[9/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/Trace.cpp.o -[10/565] Building CXX object runtime/nvqir/CMakeFiles/nvqir.dir/__/cudaq/qis/state.cpp.o -[11/565] Building CXX object runtime/nvqir/CMakeFiles/nvqir.dir/QIRTypes.cpp.o -[12/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/Executor.cpp.o -[13/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/ServerHelper.cpp.o -[14/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/Future.cpp.o -[15/565] Building CXX object runtime/common/CMakeFiles/cudaq-common.dir/NoiseModel.cpp.o -[16/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/infleqtion/CMakeFiles/cudaq-serverhelper-infleqtion.dir/InfleqtionServerHelper.cpp.o -[17/565] Building CXX object lib/Optimizer/Builder/CMakeFiles/obj.OptimBuilder.dir/Intrinsics.cpp.o -[18/565] Building CXX object lib/Optimizer/Builder/CMakeFiles/obj.OptimBuilder.dir/Factory.cpp.o -[19/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/CodeGenDialect.cpp.o -[20/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/CodeGenOps.cpp.o -[21/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/CCToLLVM.cpp.o -[22/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertCCToLLVM.cpp.o -[23/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToExecMgr.cpp.o -[24/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/Pipelines.cpp.o -[25/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/Passes.cpp.o -[26/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToQIR.cpp.o -[27/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToQIRProfile.cpp.o -[28/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/QuakeToCodegen.cpp.o -[29/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/RemoveMeasurements.cpp.o -[30/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/QuakeToExecMgr.cpp.o -[31/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertDecl.cpp.o -[32/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertStmt.cpp.o -[33/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/QuakeToLLVM.cpp.o -[34/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertType.cpp.o -[35/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/VerifyNVQIRCalls.cpp.o -[36/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/TranslateToOpenQASM.cpp.o -[37/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ConvertExpr.cpp.o -[38/565] Building CXX object lib/Frontend/nvqpp/CMakeFiles/obj.cudaq-mlirgen.dir/ASTBridge.cpp.o -[39/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/VerifyQIRProfile.cpp.o -[40/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/ConvertToQIRAPI.cpp.o -[41/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/AggressiveEarlyInlining.cpp.o -[42/565] Building CXX object lib/Optimizer/Dialect/CC/CMakeFiles/obj.CCDialect.dir/CCDialect.cpp.o -[43/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ApplyControlNegations.cpp.o -[44/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/AddDeallocs.cpp.o -[45/565] Building CXX object lib/Optimizer/CodeGen/CMakeFiles/obj.OptCodeGen.dir/WireSetsToProfileQIR.cpp.o -[46/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ApplyOpSpecialization.cpp.o -[47/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ArgumentSynthesis.cpp.o -[48/565] Building CXX object lib/Optimizer/Dialect/CC/CMakeFiles/obj.CCDialect.dir/CCOps.cpp.o -[49/565] Linking CXX static library lib/libCCDialect.a -[50/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ClassicalOptimization.cpp.o -[51/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/Decomposition.cpp.o -[52/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/CombineQuantumAlloc.cpp.o -[53/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ConstPropComplex.cpp.o -[54/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/CombineMeasurements.cpp.o -[55/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DelayMeasurements.cpp.o -[56/565] Building CXX object lib/Optimizer/Dialect/Quake/CMakeFiles/obj.QuakeDialect.dir/QuakeOps.cpp.o -[57/565] Linking CXX static library lib/libQuakeDialect.a -[58/565] Linking CXX static library lib/libOptimBuilder.a -[59/565] Linking CXX static library lib/libcudaq-mlirgen.a -[60/565] Linking CXX static library lib/libOptCodeGen.a -[61/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DecompositionPatterns.cpp.o -[62/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/BasisConversion.cpp.o -[63/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/EraseNopCalls.cpp.o -[64/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DeleteStates.cpp.o -[65/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ExpandMeasurements.cpp.o -[66/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/FactorQuantumAlloc.cpp.o -[67/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ExpandControlVeqs.cpp.o -[68/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GetConcreteMatrix.cpp.o -[69/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GenDeviceCodeLoader.cpp.o -[70/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GlobalizeArrayValues.cpp.o -[71/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopAnalysis.cpp.o -[72/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LinearCtrlRelations.cpp.o -[73/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/GenKernelExecution.cpp.o -[74/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LiftArrayAlloc.cpp.o -[75/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LambdaLifting.cpp.o -[76/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopPeeling.cpp.o -[77/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopNormalize.cpp.o -[78/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/MultiControlDecomposition.cpp.o -[79/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LoopUnroll.cpp.o -[80/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LowerToCFG.cpp.o -[81/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/LowerUnwind.cpp.o -[82/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/Mapping.cpp.o -[83/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/ObserveAnsatz.cpp.o -[84/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/PySynthCallableBlockArgs.cpp.o -[85/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/DependencyAnalysis.cpp.o -[86/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/PruneCtrlRelations.cpp.o -[87/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/MemToReg.cpp.o -[88/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/QuakeAddMetadata.cpp.o -[89/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/RefToVeqAlloc.cpp.o -[90/565] Linking CXX shared library lib/libcudaq-common.so -/usr/bin/ld: missing --end-group; added as last command line option -[91/565] Linking CXX shared library lib/libcudaq-serverhelper-infleqtion.so -[92/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/QuakeSynthesizer.cpp.o -[93/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/StatePreparation.cpp.o -[94/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/UpdateRegisterNames.cpp.o -[95/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/WriteAfterWriteElimination.cpp.o -[96/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/RegToMem.cpp.o -[97/565] Building CXX object runtime/common/CMakeFiles/cudaq-mlir-runtime.dir/ArgumentConversion.cpp.o -[98/565] Building CXX object runtime/nvqir/CMakeFiles/nvqir.dir/NVQIR.cpp.o -[99/565] Linking CXX shared library lib/libnvqir.so -[100/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/WiresToWiresets.cpp.o -[101/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-quera-qpu.dir/QuEraServerHelper.cpp.o -[102/565] Building CXX object runtime/nvqir/stim/CMakeFiles/nvqir-stim.dir/StimCircuitSimulator.cpp.o -[103/565] Linking CXX shared library lib/libnvqir-stim.so -[104/565] Building CXX object runtime/nvqir/custatevec/CMakeFiles/nvqir-custatevec-fp64.dir/CuStateVecCircuitSimulator.cpp.o -[105/565] Linking CXX shared library lib/libnvqir-custatevec-fp64.so -[106/565] Building CXX object runtime/nvqir/custatevec/CMakeFiles/nvqir-custatevec-fp32.dir/CuStateVecCircuitSimulatorF32.cpp.o -[107/565] Linking CXX shared library lib/libnvqir-custatevec-fp32.so -[108/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/tensornet_spin_op.cpp.o -[109/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/tensornet_state.cpp.o -[110/565] Building CXX object lib/Optimizer/Transforms/CMakeFiles/obj.OptTransforms.dir/UnitarySynthesis.cpp.o -[111/565] Linking CXX static library lib/libOptTransforms.a -[112/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/tn_simulation_state.cpp.o -[113/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/simulator_cutensornet.cpp.o -[114/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-quera-qpu.dir/QuEraRemoteRESTQPU.cpp.o -[115/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/tensornet_spin_op.cpp.o -[116/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet.dir/simulator_tensornet_register.cpp.o -[117/565] Building CXX object runtime/nvqir/qpp/CMakeFiles/nvqir-qpp.dir/QppCircuitSimulator.cpp.o -[118/565] Linking CXX shared library lib/libnvqir-qpp.so -[119/565] Building CXX object runtime/common/CMakeFiles/cudaq-mlir-runtime.dir/RuntimeMLIR.cpp.o -[120/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/tensornet_state.cpp.o -[121/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/tensornet-mpi-util.dir/mpi_support.cpp.o -[122/565] Building CXX object runtime/nvqir/qpp/CMakeFiles/nvqir-dm.dir/QppDMCircuitSimulator.cpp.o -[123/565] Linking CXX shared library lib/libnvqir-dm.so -[124/565] Building CXX object runtime/nvqir/cudensitymat/CMakeFiles/nvqir-dynamics.dir/mpi_support.cpp.o -[125/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/simulator_cutensornet.cpp.o -[126/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/qis/execution_manager_c_api.cpp.o -[127/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/utils/cudaq_utils.cpp.o -[128/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/utils/tensor.cpp.o -[129/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/cudaq.cpp.o -[130/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/qis/state.cpp.o -[131/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/algorithms/draw.cpp.o -[132/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/simulator_mps_register.cpp.o -[133/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/platform/quantum_platform.cpp.o -[134/565] Building CXX object runtime/nvqir/cudensitymat/CMakeFiles/nvqir-dynamics.dir/CuDensityMatSim.cpp.o -[135/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/qis/remote_state.cpp.o -[136/565] Linking CXX shared library lib/libnvqir-dynamics.so -[137/565] Building CXX object runtime/cudaq/CMakeFiles/cudaq.dir/distributed/mpi_plugin.cpp.o -[138/565] Building CXX object runtime/cudaq/qis/managers/default/CMakeFiles/cudaq-em-default.dir/DefaultExecutionManager.cpp.o -[139/565] Linking CXX shared library lib/libcudaq-em-default.so -[140/565] Building CXX object runtime/cudaq/platform/default/CMakeFiles/cudaq-platform-default.dir/DefaultQuantumPlatform.cpp.o -[141/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/ionq/IonQServerHelper.cpp.o -[142/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/infleqtion/InfleqtionServerHelper.cpp.o -[143/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/quantinuum/QuantinuumServerHelper.cpp.o -[144/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/anyon/AnyonServerHelper.cpp.o -[145/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/oqc/OQCServerHelp.cpp.o -[146/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/iqm/IQMServerHelper.cpp.o -[147/565] Linking CXX executable unittests/Optimizer/OptimizerUnitTests -[148/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/braket/BraketExecutor.cpp.o -[149/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/helpers/braket/BraketServerHelper.cpp.o -[150/565] Building CXX object runtime/cudaq/qis/managers/photonics/CMakeFiles/cudaq-em-photonics.dir/PhotonicsExecutionManager.cpp.o -[151/565] Linking CXX shared library lib/libcudaq-em-photonics.so -[152/565] Building CXX object runtime/cudaq/platform/default/rest/CMakeFiles/cudaq-rest-qpu.dir/RemoteRESTQPU.cpp.o -[153/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/ionq/CMakeFiles/cudaq-serverhelper-ionq.dir/IonQServerHelper.cpp.o -[154/565] Linking CXX shared library lib/libcudaq-serverhelper-ionq.so -[155/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/anyon/CMakeFiles/cudaq-serverhelper-anyon.dir/AnyonServerHelper.cpp.o -[156/565] Linking CXX shared library lib/libcudaq-serverhelper-anyon.so -[157/565] Building CXX object unittests/backends/quera/CMakeFiles/test_quera.dir/JsonPayloadTester.cpp.o -[158/565] Linking CXX executable unittests/backends/quera/test_quera -[159/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/iqm/CMakeFiles/cudaq-serverhelper-iqm.dir/IQMServerHelper.cpp.o -[160/565] Linking CXX shared library lib/libcudaq-serverhelper-iqm.so -[161/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaQPU.cpp.o -[162/565] Building CXX object unittests/Optimizer/CMakeFiles/test_quake_synth.dir/QuakeSynthTester.cpp.o -[163/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/oqc/CMakeFiles/cudaq-serverhelper-oqc.dir/OQCServerHelp.cpp.o -[164/565] Linking CXX shared library lib/libcudaq-serverhelper-oqc.so -[165/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/quantinuum/CMakeFiles/cudaq-serverhelper-quantinuum.dir/QuantinuumServerHelper.cpp.o -[166/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaExecutor.cpp.o -[167/565] Linking CXX shared library lib/libcudaq-serverhelper-quantinuum.so -[168/565] Building CXX object runtime/cudaq/platform/mqpu/custatevec/CMakeFiles/gpu-emulated-qpu.dir/GPUEmulatedQPU.cpp.o -[169/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/braket/CMakeFiles/cudaq-serverhelper-braket.dir/BraketServerHelper.cpp.o -[170/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaRemoteRESTQPU.cpp.o -[171/565] Building CXX object runtime/cudaq/platform/default/rest/helpers/braket/CMakeFiles/cudaq-serverhelper-braket.dir/BraketExecutor.cpp.o -[172/565] Building CXX object runtime/cudaq/platform/orca/CMakeFiles/cudaq-orca-qpu.dir/OrcaServerHelper.cpp.o -[173/565] Linking CXX shared library lib/libcudaq-serverhelper-braket.so -[174/565] Building CXX object runtime/nvqir/cutensornet/CMakeFiles/nvqir-tensornet-mps.dir/mps_simulation_state.cpp.o -[175/565] Building CXX object runtime/cudaq/platform/mqpu/CMakeFiles/cudaq-platform-mqpu.dir/MultiQPUPlatform.cpp.o -[176/565] Building CXX object runtime/cudaq/platform/mqpu/remote/CMakeFiles/cudaq-remote-simulator-qpu.dir/RemoteSimulatorQPU.cpp.o -[177/565] Building CXX object runtime/cudaq/platform/fermioniq/CMakeFiles/cudaq-fermioniq-qpu.dir/helpers/FermioniqServerHelper.cpp.o -[178/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.cc -[179/565] Building CXX object runtime/cudaq/platform/fermioniq/helpers/CMakeFiles/cudaq-serverhelper-fermioniq.dir/FermioniqServerHelper.cpp.o -[180/565] Linking CXX shared library lib/libcudaq-serverhelper-fermioniq.so -[181/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-quera-qpu.dir/QuEraExecutor.cpp.o -[182/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/callback.cpp.o -[183/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-serverhelper-quera.dir/QuEraExecutor.cpp.o -[184/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/spin_operators.cpp.o -[185/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/boson_operators.cpp.o -[186/565] Building CXX object runtime/cudaq/platform/quera/CMakeFiles/cudaq-serverhelper-quera.dir/QuEraServerHelper.cpp.o -[187/565] Linking CXX shared library lib/libcudaq-serverhelper-quera.so -[188/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/scalar_operators.cpp.o -[189/565] Building CXX object runtime/cudaq/builder/CMakeFiles/cudaq-builder.dir/QuakeValue.cpp.o -[190/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/manipulation.cpp.o -[191/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/matrix_operators.cpp.o -[192/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/helpers.cpp.o -[193/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/schedule.cpp.o -[194/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/product_operators.cpp.o -[195/565] Building CXX object runtime/cudaq/dynamics/CMakeFiles/cudaq-operator.dir/operator_sum.cpp.o -[196/565] Linking CXX shared library lib/libcudaq-operator.so -[197/565] Linking CXX shared library lib/libcudaq.so -[198/565] Building CXX object runtime/test/CMakeFiles/test_argument_conversion.dir/test_argument_conversion.cpp.o -[199/565] Linking CXX shared library lib/libnvqir-tensornet.so -[200/565] Linking CXX shared library lib/libnvqir-tensornet-mps.so -[201/565] Linking CXX shared library lib/libcudaq-platform-default.so -[202/565] Linking CXX shared library lib/libcudaq-platform-mqpu.so -[203/565] Linking CXX executable bin/cudaq-lsp-server -[204/565] Building CXX object runtime/cudaq/platform/default/rest_server/CMakeFiles/rest-remote-platform-client.dir/helpers/RestRemoteClient.cpp.o -[205/565] Building CXX object tools/cudaq-opt/CMakeFiles/cudaq-opt.dir/cudaq-opt.cpp.o -[206/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.quake.ops_gen -[207/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.quake -[208/565] Linking CXX executable bin/CircuitCheck -[209/565] cd /workspaces/cuda-quantum/build/release/python && /usr/local/cmake-3.26/bin/cmake -E copy_directory /workspaces/cuda-quantum/python /workspaces/cuda-quantum/build/release/python && /usr/local/cmake-3.26/bin/cmake -DMETADATA_FILE="/workspaces/cuda-quantum/build/release/python/cudaq/_metadata.py" -DCUDA_VERSION_MAJOR=12 -P /workspaces/cuda-quantum/python/metadata.cmake --- Creating metadata file in /workspaces/cuda-quantum/build/release/python/cudaq/_metadata.py. -[210/565] Building CXX object runtime/cudaq/builder/CMakeFiles/cudaq-builder.dir/kernel_builder.cpp.o -[211/565] Linking CXX executable bin/cudaq-opt -[212/565] Building CXX object tools/cudaq-translate/CMakeFiles/cudaq-translate.dir/cudaq-translate.cpp.o -[213/565] Building CXX object tools/cudaq-qpud/CMakeFiles/cudaq-qpud.dir/RestServerMain.cpp.o -[214/565] Linking CXX shared library lib/libcudaq-mlir-runtime.so -[215/565] Linking CXX shared library lib/libcudaq-rest-qpu.so -[216/565] Linking CXX shared library lib/librest-remote-platform-client.so -[217/565] Building CXX object runtime/cudaq/platform/fermioniq/CMakeFiles/cudaq-fermioniq-qpu.dir/FermioniqQPU.cpp.o -[218/565] Linking CXX shared library lib/libcudaq-orca-qpu.so -[219/565] Linking CXX shared library lib/libcudaq-remote-simulator-qpu.so -[220/565] Linking CXX shared library lib/libcudaq-fermioniq-qpu.so -[221/565] Linking CXX shared library lib/libcudaq-quera-qpu.so -[222/565] Linking CXX shared library lib/libcudaq-builder.so -[223/565] Linking CXX executable bin/test_argument_conversion -[224/565] Building CXX object runtime/cudaq/platform/default/rest_server/CMakeFiles/rest-remote-platform-server.dir/helpers/RestRemoteServer.cpp.o -[225/565] Linking CXX executable bin/cudaq-translate -[226/565] Linking CXX shared library lib/librest-remote-platform-server.so -[227/565] Linking CXX executable bin/cudaq-qpud -[228/565] Building CXX object python/utils/CMakeFiles/cudaq-py-utils.dir/LinkedLibraryHolder.cpp.o -[229/565] Linking CXX shared library lib/libcudaq-py-utils.so -[230/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_draw.cpp.o -[231/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_AnalogHamiltonian.cpp.o -[232/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_optimizer.cpp.o -[233/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_observe_async.cpp.o -[234/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_sample_async.cpp.o -[235/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_translate.cpp.o -[236/565] Building CXX object tools/cudaq-quake/CMakeFiles/cudaq-quake.dir/cudaq-quake.cpp.o -[237/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/qis/py_qubit_qis.cpp.o -[238/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/target/py_testing_utils.cpp.o -[239/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_state.cpp.o -[240/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_evolve.cpp.o -[241/565] Linking CXX executable bin/cudaq-quake -[242/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/algorithms/py_vqe.cpp.o -[243/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyRemoteSimulatorQPU.cpp.o -[244/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaQPU.cpp.o -[245/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/utils/LinkedLibraryHolder.cpp.o -[246/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaExecutor.cpp.o -[247/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.async_dialect.ops_gen -[248/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.async_dialect -[249/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.bufferization -[250/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.bufferization.ops_gen -[251/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.builtin -[252/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.builtin.ops_gen -[253/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.complex -[254/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.complex.ops_gen -[255/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.cf -[256/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.cf.ops_gen -[257/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.func -[258/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.func.ops_gen -[259/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.gpu -[260/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.gpu.ops_gen -[261/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.linalg.ops_gen -[262/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.linalg -[263/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaServerHelper.cpp.o -[264/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/cudaq/platform/orca/OrcaRemoteRESTQPU.cpp.o -[265/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/__/runtime/common/ArgumentConversion.cpp.o -[266/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/mlir/py_register_dialects.cpp.o -[267/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyFermioniqRESTQPU.cpp.o -[268/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyQuEraRemoteRESTQPU.cpp.o -[269/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.transform -[270/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.transform.ops_gen -[271/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.loop_transform -[272/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.loop_transform.ops_gen -[273/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.structured_transform -[274/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.structured_transform.ops_gen -[275/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.math -[276/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.math.ops_gen -[277/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.arith -[278/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.arith.ops_gen -[279/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.memref -[280/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.memref.ops_gen -[281/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.ml_program -[282/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.ml_program.ops_gen -[283/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.quant -[284/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.pdl -[285/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.pdl.ops_gen -[286/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.scf -[287/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.scf.ops_gen -[288/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.shape -[289/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.shape.ops_gen -[290/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.sparse_tensor -[291/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.sparse_tensor.ops_gen -[292/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tensor -[293/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tensor.ops_gen -[294/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tosa -[295/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.tosa.ops_gen -[296/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.vector -[297/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Dialects.vector.ops_gen -[298/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.Core -[299/565] Running utility command for CUDAQuantumPythonModules.sources.MLIRPythonSources.ExecutionEngine -[300/565] Building dialects/_cc_ops_gen.py... -[301/565] Running utility command for CUDAQuantumPythonModules.sources.CUDAQuantumPythonSources.cc.ops_gen -[302/565] Linking CXX shared library lib/plugins/libcudaq-pyscf.so -[303/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/cudaq/platform/py_alt_launch_kernel.cpp.o -[304/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_ExecutionContext.cpp.o -[305/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_EvolveResult.cpp.o -[306/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_NoiseModel.cpp.o -[307/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/common/py_ObserveResult.cpp.o -[308/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyRestRemoteClient.cpp.o -[309/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/adjoint_tester.cpp.o -[310/565] Building CXX object python/tests/interop/CMakeFiles/cudaq_test_cpp_algo.dir/test_cpp_quantum_algorithm_module.cpp.o -[311/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/ccnot_tester.cpp.o -[312/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/CUDAQuantumExtension.cpp.o -[313/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/deuteron_variational_tester.cpp.o -[314/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/draw_tester.cpp.o -[315/565] Linking CXX shared library python/cudaq/mlir/_mlir_libs/libCUDAQuantumPythonCAPI.so -[316/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/ghz_nisq_tester.cpp.o -[317/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/gradient_tester.cpp.o -[318/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/grover_test.cpp.o -[319/565] Building CXX object python/runtime/interop/CMakeFiles/cudaq-python-interop.dir/PythonCppInterop.cpp.o -[320/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/qpe_ftqc.cpp.o -[321/565] Building CXX object python/extension/CMakeFiles/CUDAQuantumPythonModules.extension._quakeDialects.dso.dir/__/runtime/utils/PyRemoteRESTQPU.cpp.o -[322/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/nlopt_tester.cpp.o -[323/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/qpe_nisq.cpp.o -[324/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirRegisterEverything.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[325/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirAsyncPasses.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[326/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/builder_tester.cpp.o -[327/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirLinalgPasses.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[328/565] Linking CXX shared library lib/libcudaq-python-interop.so -[329/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirGPUPasses.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[330/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsLinalg.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[331/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsTransform.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 5 LTRANS jobs -[332/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsPDL.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 5 LTRANS jobs -[333/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsQuant.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 6 LTRANS jobs -[334/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirSparseTensorPasses.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[335/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/qubit_allocation.cpp.o -[336/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirDialectsSparseTensor.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 6 LTRANS jobs -[337/565] Linking CXX shared module python/tests/interop/cudaq_test_cpp_algo.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 4 LTRANS jobs -[338/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/bug67_vqe_then_sample.cpp.o -[339/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 5 LTRANS jobs -[340/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/bug77_vqe_with_shots.cpp.o -[341/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/bug116_cusv_measure_bug.cpp.o -[342/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/vqe_tester.cpp.o -[343/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/noise_tester.cpp.o -[344/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/negative_controls_tester.cpp.o -[345/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/async_tester.cpp.o -[346/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/get_state_tester.cpp.o -[347/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/observe_result_tester.cpp.o -[348/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/measure_reset_tester.cpp.o -[349/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/qir/NVQIRTester.cpp.o -[350/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/common/MeasureCountsTester.cpp.o -[351/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/kernels_tester.cpp.o -[352/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/qpe_ftqc.cpp.o -[353/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/common/NoiseModelTester.cpp.o -[354/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/tracer_tester.cpp.o -[355/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/integration/gate_library_tester.cpp.o -[356/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/qis/QubitQISTester.cpp.o -[357/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/adjoint_tester.cpp.o -[358/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/ccnot_tester.cpp.o -[359/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/draw_tester.cpp.o -[360/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/deuteron_variational_tester.cpp.o -[361/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/gradient_tester.cpp.o -[362/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/ghz_nisq_tester.cpp.o -[363/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/grover_test.cpp.o -[364/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/nlopt_tester.cpp.o -[365/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/vqe_tester.cpp.o -[366/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/qpe_nisq.cpp.o -[367/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/bug67_vqe_then_sample.cpp.o -[368/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/qubit_allocation.cpp.o -[369/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/async_tester.cpp.o -[370/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/bug77_vqe_with_shots.cpp.o -[371/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/bug116_cusv_measure_bug.cpp.o -[372/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/builder_tester.cpp.o -[373/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/negative_controls_tester.cpp.o -[374/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/observe_result_tester.cpp.o -[375/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/measure_reset_tester.cpp.o -[376/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/qis/QubitQISTester.cpp.o -[377/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/qir/NVQIRTester.cpp.o -[378/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/get_state_tester.cpp.o -[379/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/common/MeasureCountsTester.cpp.o -[380/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/kernels_tester.cpp.o -[381/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/noise_tester.cpp.o -[382/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/gate_library_tester.cpp.o -[383/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/common/NoiseModelTester.cpp.o -[384/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/integration/tracer_tester.cpp.o -[385/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_mlir.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 34 LTRANS jobs -[386/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/ccnot_tester.cpp.o -[387/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/deuteron_variational_tester.cpp.o -[388/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/adjoint_tester.cpp.o -[389/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/gradient_tester.cpp.o -[390/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/draw_tester.cpp.o -[391/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/grover_test.cpp.o -[392/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/ghz_nisq_tester.cpp.o -[393/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/nlopt_tester.cpp.o -[394/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/qpe_ftqc.cpp.o -[395/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/qpe_nisq.cpp.o -[396/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/builder_tester.cpp.o -[397/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/qubit_allocation.cpp.o -[398/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/vqe_tester.cpp.o -[399/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/bug67_vqe_then_sample.cpp.o -[400/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/bug77_vqe_with_shots.cpp.o -[401/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/bug116_cusv_measure_bug.cpp.o -[402/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/observe_result_tester.cpp.o -[403/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/async_tester.cpp.o -[404/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/negative_controls_tester.cpp.o -[405/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/get_state_tester.cpp.o -[406/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/measure_reset_tester.cpp.o -[407/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/qir/NVQIRTester.cpp.o -[408/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/noise_tester.cpp.o -[409/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/common/MeasureCountsTester.cpp.o -[410/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/qis/QubitQISTester.cpp.o -[411/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/kernels_tester.cpp.o -[412/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/common/NoiseModelTester.cpp.o -[413/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/gate_library_tester.cpp.o -[414/565] Building CXX object unittests/CMakeFiles/test_runtime_qpp.dir/backends/QPPTester.cpp.o -[415/565] Linking CXX executable unittests/test_runtime_qpp -[416/565] Building CXX object unittests/CMakeFiles/test_runtime_stim.dir/integration/tracer_tester.cpp.o -[417/565] Linking CXX executable unittests/test_runtime_stim -[418/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/adjoint_tester.cpp.o -[419/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/ccnot_tester.cpp.o -[420/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/draw_tester.cpp.o -[421/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/deuteron_variational_tester.cpp.o -[422/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/gradient_tester.cpp.o -[423/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/ghz_nisq_tester.cpp.o -[424/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/grover_test.cpp.o -[425/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/nlopt_tester.cpp.o -[426/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/qpe_ftqc.cpp.o -[427/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/qpe_nisq.cpp.o -[428/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/qubit_allocation.cpp.o -[429/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/bug77_vqe_with_shots.cpp.o -[430/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/bug116_cusv_measure_bug.cpp.o -[431/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/bug67_vqe_then_sample.cpp.o -[432/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/vqe_tester.cpp.o -[433/565] Linking CXX shared module python/cudaq/mlir/_mlir_libs/_quakeDialects.cpython-310-x86_64-linux-gnu.so -lto-wrapper: warning: using serial compilation of 54 LTRANS jobs -[434/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/async_tester.cpp.o -[435/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/negative_controls_tester.cpp.o -[436/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/noise_tester.cpp.o -[437/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/builder_tester.cpp.o -[438/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/measure_reset_tester.cpp.o -[439/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/qir/NVQIRTester.cpp.o -[440/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/get_state_tester.cpp.o -[441/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/observe_result_tester.cpp.o -[442/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/common/MeasureCountsTester.cpp.o -[443/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/kernels_tester.cpp.o -[444/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/common/NoiseModelTester.cpp.o -[445/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/tracer_tester.cpp.o -[446/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/integration/gate_library_tester.cpp.o -[447/565] Building CXX object unittests/CMakeFiles/test_mqpu.dir/mqpu/mqpu_tester.cpp.o -[448/565] Building CXX object unittests/CMakeFiles/test_runtime_dm.dir/backends/QPPDMTester.cpp.o -[449/565] Linking CXX executable unittests/test_mqpu -[450/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/deuteron_variational_tester.cpp.o -[451/565] Linking CXX executable unittests/test_runtime_dm -[452/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/gradient_tester.cpp.o -[453/565] Building CXX object unittests/CMakeFiles/test_runtime_custatevec-fp32.dir/qis/QubitQISTester.cpp.o -[454/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/nlopt_tester.cpp.o -[455/565] Linking CXX executable unittests/test_runtime_custatevec-fp32 -[456/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/adjoint_tester.cpp.o -[457/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/draw_tester.cpp.o -[458/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/gradient_tester.cpp.o -[459/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/ccnot_tester.cpp.o -[460/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/deuteron_variational_tester.cpp.o -[461/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/grover_test.cpp.o -[462/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/ghz_nisq_tester.cpp.o -[463/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/nlopt_tester.cpp.o -[464/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/qpe_ftqc.cpp.o -[465/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/vqe_tester.cpp.o -[466/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/bug67_vqe_then_sample.cpp.o -[467/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/qpe_nisq.cpp.o -[468/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/qubit_allocation.cpp.o -[469/565] Building CXX object unittests/CMakeFiles/test_custatevec_observe_from_sampling.dir/integration/builder_tester.cpp.o -[470/565] Linking CXX executable unittests/test_custatevec_observe_from_sampling -[471/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/bug77_vqe_with_shots.cpp.o -[472/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/bug116_cusv_measure_bug.cpp.o -[473/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/builder_tester.cpp.o -[474/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/negative_controls_tester.cpp.o -[475/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/async_tester.cpp.o -[476/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/measure_reset_tester.cpp.o -[477/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/qir/NVQIRTester.cpp.o -[478/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/get_state_tester.cpp.o -[479/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/observe_result_tester.cpp.o -[480/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/common/MeasureCountsTester.cpp.o -[481/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/kernels_tester.cpp.o -[482/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/noise_tester.cpp.o -[483/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/common/NoiseModelTester.cpp.o -[484/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/tracer_tester.cpp.o -[485/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/adjoint_tester.cpp.o -[486/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/integration/gate_library_tester.cpp.o -[487/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet.dir/qis/QubitQISTester.cpp.o -[488/565] Linking CXX executable unittests/test_runtime_tensornet -[489/565] Building CUDA object unittests/CMakeFiles/test_gpu_get_state.dir/gpu/get_state_tester.cu.o -[490/565] Linking CXX executable unittests/test_gpu_get_state -[491/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/draw_tester.cpp.o -[492/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/gradient_tester.cpp.o -[493/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/ccnot_tester.cpp.o -[494/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/deuteron_variational_tester.cpp.o -[495/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/nlopt_tester.cpp.o -[496/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/ghz_nisq_tester.cpp.o -[497/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/grover_test.cpp.o -[498/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/qpe_ftqc.cpp.o -[499/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/vqe_tester.cpp.o -[500/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/bug67_vqe_then_sample.cpp.o -[501/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/qpe_nisq.cpp.o -[502/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/bug116_cusv_measure_bug.cpp.o -[503/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/bug77_vqe_with_shots.cpp.o -[504/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/qubit_allocation.cpp.o -[505/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/builder_tester.cpp.o -[506/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/negative_controls_tester.cpp.o -[507/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/async_tester.cpp.o -[508/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/measure_reset_tester.cpp.o -[509/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/get_state_tester.cpp.o -[510/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/observe_result_tester.cpp.o -[511/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/qir/NVQIRTester.cpp.o -[512/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/common/MeasureCountsTester.cpp.o -[513/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/noise_tester.cpp.o -[514/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/common/NoiseModelTester.cpp.o -[515/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/kernels_tester.cpp.o -[516/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/tracer_tester.cpp.o -[517/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/noise_tester.cpp.o -[518/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/utils.cpp.o -[519/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/integration/gate_library_tester.cpp.o -[520/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/deuteron_variational_tester.cpp.o -[521/565] Building CXX object unittests/CMakeFiles/test_runtime_tensornet-mps.dir/qis/QubitQISTester.cpp.o -[522/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/observe_result_tester.cpp.o -[523/565] Linking CXX executable unittests/test_runtime_tensornet-mps -[524/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/scalar_operator.cpp.o -[525/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/operator_conversions.cpp.o -[526/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/spin_operator.cpp.o -[527/565] Building CXX object unittests/CMakeFiles/test_qudit.dir/qudit/SimpleQuditTester.cpp.o -[528/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/matrix_operator.cpp.o -[529/565] Building CXX object unittests/CMakeFiles/test_utils.dir/utils/Tensor.cpp.o -[530/565] Building CXX object unittests/CMakeFiles/test_photonics.dir/photonics/PhotonicsTester.cpp.o -[531/565] Linking CXX executable unittests/test_photonics -[532/565] Building CXX object unittests/CMakeFiles/test_mpi_plugin.dir/mpi/mpi_tester.cpp.o -[533/565] Linking CXX executable unittests/test_mpi_plugin -[534/565] Building CXX object unittests/CMakeFiles/test_tensornet_observe_path_reuse.dir/integration/builder_tester.cpp.o -[535/565] Building CXX object unittests/CMakeFiles/test_domains.dir/domains/ChemistryTester.cpp.o -[536/565] Linking CXX executable unittests/test_domains -[537/565] Linking CXX executable unittests/test_tensornet_observe_path_reuse -[538/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/operator_sum.cpp.o -[539/565] Building CXX object unittests/CMakeFiles/test_operators.dir/dynamics/product_operator.cpp.o -[540/565] Linking CXX executable unittests/test_operators -[541/565] Building CXX object unittests/backends/infleqtion/CMakeFiles/test_infleqtion.dir/InfleqtionTester.cpp.o -[542/565] Linking CXX executable unittests/backends/infleqtion/test_infleqtion -[543/565] Building CXX object unittests/backends/ionq/CMakeFiles/test_ionq.dir/IonQTester.cpp.o -[544/565] Building CXX object unittests/CMakeFiles/test_utils.dir/utils/UtilsTester.cpp.o -[545/565] Linking CXX executable unittests/test_utils -[546/565] Linking CXX executable unittests/backends/ionq/test_ionq -[547/565] Building CXX object unittests/backends/anyon/CMakeFiles/test_anyon.dir/AnyonTester.cpp.o -[548/565] Linking CXX executable unittests/Optimizer/test_quake_synth -[549/565] Linking CXX executable unittests/backends/anyon/test_anyon -[550/565] Building CXX object unittests/backends/braket/CMakeFiles/test_braket.dir/BraketTester.cpp.o -[551/565] Linking CXX executable unittests/backends/braket/test_braket -[552/565] Building CXX object unittests/backends/oqc/CMakeFiles/test_oqc.dir/OQCTester.cpp.o -[553/565] Linking CXX executable unittests/backends/oqc/test_oqc -[554/565] Building CXX object unittests/backends/iqm/CMakeFiles/test_iqm.dir/IQMTester.cpp.o -[555/565] Linking CXX executable unittests/backends/iqm/test_iqm -[556/565] Building CXX object unittests/backends/quantinuum/CMakeFiles/test_quantinuum.dir/QuantinuumTester.cpp.o -[557/565] Linking CXX executable unittests/backends/quantinuum/test_quantinuum -[558/565] Building CXX object unittests/qudit/simple_qudit/CMakeFiles/cudaq-em-qudit.dir/SimpleQuditExecutionManager.cpp.o -[559/565] Linking CXX shared library unittests/qudit/simple_qudit/libcudaq-em-qudit.so -[560/565] Linking CXX executable unittests/test_qudit -[561/565] Building CXX object unittests/backends/qpp_observe/CMakeFiles/nvqir-qpp-observe-test.dir/QPPObserveBackend.cpp.o -[562/565] Linking CXX shared library unittests/backends/qpp_observe/libnvqir-qpp-observe-test.so -[563/565] Building CXX object unittests/backends/qpp_observe/CMakeFiles/test_observe_backend.dir/QppObserveTester.cpp.o -[564/565] Linking CXX executable unittests/backends/qpp_observe/test_observe_backend -[564/565] Install the project... --- Install configuration: "Release" --- Installing: /usr/local/cudaq/lib/plugins/libcudaq-comm-plugin.so --- Up-to-date: /usr/local/cudaq/lib/libfmt.a --- Up-to-date: /usr/local/cudaq/include/fmt/args.h --- Up-to-date: /usr/local/cudaq/include/fmt/chrono.h --- Up-to-date: /usr/local/cudaq/include/fmt/color.h --- Up-to-date: /usr/local/cudaq/include/fmt/compile.h --- Up-to-date: /usr/local/cudaq/include/fmt/core.h --- Up-to-date: /usr/local/cudaq/include/fmt/format.h --- Up-to-date: /usr/local/cudaq/include/fmt/format-inl.h --- Up-to-date: /usr/local/cudaq/include/fmt/os.h --- Up-to-date: /usr/local/cudaq/include/fmt/ostream.h --- Up-to-date: /usr/local/cudaq/include/fmt/printf.h --- Up-to-date: /usr/local/cudaq/include/fmt/ranges.h --- Up-to-date: /usr/local/cudaq/include/fmt/std.h --- Up-to-date: /usr/local/cudaq/include/fmt/xchar.h --- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-config.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-config-version.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-targets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/fmt/fmt-targets-release.cmake --- Up-to-date: /usr/local/cudaq/lib/pkgconfig/fmt.pc --- Up-to-date: /usr/local/cudaq/lib/libcpr.a --- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprTargets-release.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cpr/cprConfigVersion.cmake --- Up-to-date: /usr/local/cudaq/include/cpr --- Up-to-date: /usr/local/cudaq/include/cpr/verbose.h --- Up-to-date: /usr/local/cudaq/include/cpr/error.h --- Up-to-date: /usr/local/cudaq/include/cpr/payload.h --- Up-to-date: /usr/local/cudaq/include/cpr/proxies.h --- Up-to-date: /usr/local/cudaq/include/cpr/buffer.h --- Up-to-date: /usr/local/cudaq/include/cpr/local_port.h --- Up-to-date: /usr/local/cudaq/include/cpr/parameters.h --- Up-to-date: /usr/local/cudaq/include/cpr/curl_container.h --- Up-to-date: /usr/local/cudaq/include/cpr/bearer.h --- Up-to-date: /usr/local/cudaq/include/cpr/low_speed.h --- Up-to-date: /usr/local/cudaq/include/cpr/reserve_size.h --- Up-to-date: /usr/local/cudaq/include/cpr/ssl_options.h --- Up-to-date: /usr/local/cudaq/include/cpr/response.h --- Up-to-date: /usr/local/cudaq/include/cpr/http_version.h --- Up-to-date: /usr/local/cudaq/include/cpr/connect_timeout.h --- Up-to-date: /usr/local/cudaq/include/cpr/redirect.h --- Up-to-date: /usr/local/cudaq/include/cpr/timeout.h --- Up-to-date: /usr/local/cudaq/include/cpr/interface.h --- Up-to-date: /usr/local/cudaq/include/cpr/file.h --- Up-to-date: /usr/local/cudaq/include/cpr/interceptor.h --- Up-to-date: /usr/local/cudaq/include/cpr/body.h --- Up-to-date: /usr/local/cudaq/include/cpr/session.h --- Up-to-date: /usr/local/cudaq/include/cpr/cprtypes.h --- Up-to-date: /usr/local/cudaq/include/cpr/callback.h --- Up-to-date: /usr/local/cudaq/include/cpr/range.h --- Up-to-date: /usr/local/cudaq/include/cpr/multipart.h --- Up-to-date: /usr/local/cudaq/include/cpr/threadpool.h --- Up-to-date: /usr/local/cudaq/include/cpr/accept_encoding.h --- Up-to-date: /usr/local/cudaq/include/cpr/cookies.h --- Up-to-date: /usr/local/cudaq/include/cpr/auth.h --- Up-to-date: /usr/local/cudaq/include/cpr/proxyauth.h --- Up-to-date: /usr/local/cudaq/include/cpr/local_port_range.h --- Up-to-date: /usr/local/cudaq/include/cpr/user_agent.h --- Up-to-date: /usr/local/cudaq/include/cpr/singleton.h --- Up-to-date: /usr/local/cudaq/include/cpr/status_codes.h --- Up-to-date: /usr/local/cudaq/include/cpr/cpr.h --- Up-to-date: /usr/local/cudaq/include/cpr/api.h --- Up-to-date: /usr/local/cudaq/include/cpr/ssl_ctx.h --- Up-to-date: /usr/local/cudaq/include/cpr/limit_rate.h --- Up-to-date: /usr/local/cudaq/include/cpr/cert_info.h --- Up-to-date: /usr/local/cudaq/include/cpr/async.h --- Up-to-date: /usr/local/cudaq/include/cpr/util.h --- Up-to-date: /usr/local/cudaq/include/cpr/curlholder.h --- Up-to-date: /usr/local/cudaq/include/cpr/unix_socket.h --- Up-to-date: /usr/local/cudaq/include/cpr --- Up-to-date: /usr/local/cudaq/include/cpr/cprver.h --- Up-to-date: /usr/local/cudaq/include --- Up-to-date: /usr/local/cudaq/include/crow --- Up-to-date: /usr/local/cudaq/include/crow/query_string.h --- Up-to-date: /usr/local/cudaq/include/crow/logging.h --- Up-to-date: /usr/local/cudaq/include/crow/compression.h --- Up-to-date: /usr/local/cudaq/include/crow/http_request.h --- Up-to-date: /usr/local/cudaq/include/crow/app.h --- Up-to-date: /usr/local/cudaq/include/crow/TinySHA1.hpp --- Up-to-date: /usr/local/cudaq/include/crow/socket_adaptors.h --- Up-to-date: /usr/local/cudaq/include/crow/http_response.h --- Up-to-date: /usr/local/cudaq/include/crow/parser.h --- Up-to-date: /usr/local/cudaq/include/crow/task_timer.h --- Up-to-date: /usr/local/cudaq/include/crow/settings.h --- Up-to-date: /usr/local/cudaq/include/crow/http_server.h --- Up-to-date: /usr/local/cudaq/include/crow/returnable.h --- Up-to-date: /usr/local/cudaq/include/crow/middleware.h --- Up-to-date: /usr/local/cudaq/include/crow/routing.h --- Up-to-date: /usr/local/cudaq/include/crow/common.h --- Up-to-date: /usr/local/cudaq/include/crow/ci_map.h --- Up-to-date: /usr/local/cudaq/include/crow/websocket.h --- Up-to-date: /usr/local/cudaq/include/crow/json.h --- Up-to-date: /usr/local/cudaq/include/crow/version.h --- Up-to-date: /usr/local/cudaq/include/crow/http_parser_merged.h --- Up-to-date: /usr/local/cudaq/include/crow/multipart.h --- Up-to-date: /usr/local/cudaq/include/crow/mustache.h --- Up-to-date: /usr/local/cudaq/include/crow/mime_types.h --- Up-to-date: /usr/local/cudaq/include/crow/middlewares --- Up-to-date: /usr/local/cudaq/include/crow/middlewares/cookie_parser.h --- Up-to-date: /usr/local/cudaq/include/crow/middlewares/session.h --- Up-to-date: /usr/local/cudaq/include/crow/middlewares/cors.h --- Up-to-date: /usr/local/cudaq/include/crow/middlewares/utf-8.h --- Up-to-date: /usr/local/cudaq/include/crow/http_connection.h --- Up-to-date: /usr/local/cudaq/include/crow/middleware_context.h --- Up-to-date: /usr/local/cudaq/include/crow/utility.h --- Up-to-date: /usr/local/cudaq/include/crow.h --- Up-to-date: /usr/local/cudaq/lib/cmake/Crow/CrowTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/Crow/Findasio.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/Crow/CrowConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQCommonConfig.cmake --- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmDefaultConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQNloptConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQSpinConfig.cmake --- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQOperatorConfig.cmake --- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEnsmallenConfig.cmake --- Installing: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPlatformDefaultConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPythonInteropConfig.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeCUDAQCompiler.cmake.in --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeCUDAQInformation.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeDetermineCUDAQCompiler.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CMakeTestCUDAQCompiler.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/nvqir/NVQIRConfig.cmake --- Installing: /usr/local/cudaq/lib/libcudaq-mlirgen.a --- Installing: /usr/local/cudaq/lib/libOptimBuilder.a --- Installing: /usr/local/cudaq/lib/libOptCodeGen.a --- Up-to-date: /usr/local/cudaq/lib/libOptCodeGen.a --- Installing: /usr/local/cudaq/lib/libCCDialect.a --- Up-to-date: /usr/local/cudaq/lib/libCCDialect.a --- Installing: /usr/local/cudaq/lib/libQuakeDialect.a --- Up-to-date: /usr/local/cudaq/lib/libQuakeDialect.a --- Installing: /usr/local/cudaq/lib/libOptTransforms.a --- Up-to-date: /usr/local/cudaq/lib/libOptTransforms.a --- Up-to-date: /usr/local/cudaq/lib/libCUDAQuantumMLIRCAPI.a --- Up-to-date: /usr/local/cudaq/lib/objects-Release/obj.CUDAQuantumMLIRCAPI/Dialects.cpp.o --- Up-to-date: /usr/local/cudaq/lib/libCUDAQSupport.a --- Up-to-date: /usr/local/cudaq/lib/libCUDAQTargetConfigUtil.a --- Installing: /usr/local/cudaq/lib/libcudaq-common.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQCommonTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQCommonTargets-release.cmake --- Up-to-date: /usr/local/cudaq/./cacert.pem --- Installing: /usr/local/cudaq/lib/libcudaq-mlir-runtime.so --- Installing: /usr/local/cudaq/lib/libnvqir.so --- Installing: /usr/local/cudaq/lib/libnvqir-qpp.so --- Installing: /usr/local/cudaq/lib/libnvqir-dm.so --- Up-to-date: /usr/local/cudaq/targets/qpp-cpu.yml --- Up-to-date: /usr/local/cudaq/targets/density-matrix-cpu.yml --- Installing: /usr/local/cudaq/lib/libnvqir-stim.so --- Up-to-date: /usr/local/cudaq/targets/stim.yml --- Up-to-date: /usr/local/cudaq/lib/libnvqir-custatevec-kernels.so --- Installing: /usr/local/cudaq/lib/libnvqir-custatevec-fp64.so --- Installing: /usr/local/cudaq/lib/libnvqir-custatevec-fp32.so --- Up-to-date: /usr/local/cudaq/include/nvqir/CuStateVecCircuitSimulator.h --- Installing: /usr/local/cudaq/lib/libnvqir-tensornet.so --- Up-to-date: /usr/local/cudaq/targets/tensornet.yml --- Installing: /usr/local/cudaq/lib/libnvqir-tensornet-mps.so --- Up-to-date: /usr/local/cudaq/targets/tensornet-mps.yml --- Installing: /usr/local/cudaq/lib/libnvqir-dynamics.so --- Up-to-date: /usr/local/cudaq/lib/cmake/nvqir/NVQIRTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/nvqir/NVQIRTargets-release.cmake --- Up-to-date: /usr/local/cudaq/lib/libcudaq-spin.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQSpinTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQSpinTargets-release.cmake --- Installing: /usr/local/cudaq/lib/libcudaq-em-default.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmDefaultTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmDefaultTargets-release.cmake --- Installing: /usr/local/cudaq/lib/libcudaq-em-photonics.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmPhotonicsTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEmPhotonicsTargets-release.cmake --- Up-to-date: /usr/local/cudaq/targets/orca-photonics.yml --- Up-to-date: /usr/local/cudaq/lib/libcudaq-nlopt.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQNloptTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQNloptTargets-release.cmake --- Up-to-date: /usr/local/cudaq/lib/libcudaq-ensmallen.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEnsmallenTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQEnsmallenTargets-release.cmake --- Installing: /usr/local/cudaq/lib/libcudaq-platform-default.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPlatformDefaultTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPlatformDefaultTargets-release.cmake --- Installing: /usr/local/cudaq/targets/anyon.yml --- Up-to-date: /usr/local/cudaq/targets/mapping/anyon/berkeley-25q.txt --- Up-to-date: /usr/local/cudaq/targets/mapping/anyon/telegraph-8q.txt --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-anyon.so --- Installing: /usr/local/cudaq/targets/infleqtion.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-infleqtion.so --- Installing: /usr/local/cudaq/targets/ionq.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-ionq.so --- Installing: /usr/local/cudaq/targets/iqm.yml --- Up-to-date: /usr/local/cudaq/targets/mapping/iqm/Adonis.txt --- Up-to-date: /usr/local/cudaq/targets/mapping/iqm/Apollo.txt --- Up-to-date: /usr/local/cudaq/targets/mapping/iqm/Aphrodite.txt --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-iqm.so --- Installing: /usr/local/cudaq/targets/oqc.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-oqc.so --- Up-to-date: /usr/local/cudaq/targets/mapping/oqc/lucy.txt --- Up-to-date: /usr/local/cudaq/targets/mapping/oqc/toshiko.txt --- Installing: /usr/local/cudaq/targets/quantinuum.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-quantinuum.so --- Installing: /usr/local/cudaq/targets/braket.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-braket.so --- Installing: /usr/local/cudaq/lib/libcudaq-rest-qpu.so --- Installing: /usr/local/cudaq/lib/librest-remote-platform-client.so --- Installing: /usr/local/cudaq/lib/librest-remote-platform-server.so --- Up-to-date: /usr/local/cudaq/targets/opt-test.yml --- Up-to-date: /usr/local/cudaq/targets/nvidia.yml --- Up-to-date: /usr/local/cudaq/targets/nvidia-fp64.yml --- Installing: /usr/local/cudaq/lib/libcudaq-orca-qpu.so --- Up-to-date: /usr/local/cudaq/targets/orca.yml --- Installing: /usr/local/cudaq/lib/libcudaq-remote-simulator-qpu.so --- Up-to-date: /usr/local/cudaq/targets/nvidia-mqpu.yml --- Up-to-date: /usr/local/cudaq/targets/nvidia-mqpu-mps.yml --- Up-to-date: /usr/local/cudaq/targets/nvidia-mqpu-fp64.yml --- Installing: /usr/local/cudaq/lib/libcudaq-platform-mqpu.so --- Up-to-date: /usr/local/cudaq/targets/remote-mqpu.yml --- Up-to-date: /usr/local/cudaq/targets/nvqc.yml --- Installing: /usr/local/cudaq/lib/libcudaq-fermioniq-qpu.so --- Installing: /usr/local/cudaq/targets/fermioniq.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-fermioniq.so --- Installing: /usr/local/cudaq/lib/libcudaq-quera-qpu.so --- Installing: /usr/local/cudaq/targets/quera.yml --- Installing: /usr/local/cudaq/lib/libcudaq-serverhelper-quera.so --- Installing: /usr/local/cudaq/lib/libcudaq-builder.so --- Up-to-date: /usr/local/cudaq/lib/libcudaq-chemistry.so --- Installing: /usr/local/cudaq/lib/libcudaq-operator.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQOperatorTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQOperatorTargets-release.cmake --- Installing: /usr/local/cudaq/lib/libcudaq.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQTargets-release.cmake --- Installing: /usr/local/cudaq/distributed_interfaces/distributed_capi.h --- Installing: /usr/local/cudaq/distributed_interfaces/mpi_comm_impl.cpp --- Up-to-date: /usr/local/cudaq/distributed_interfaces/activate_custom_mpi.sh --- Up-to-date: /usr/local/cudaq/include/cudaq --- Up-to-date: /usr/local/cudaq/include/cudaq/platform --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/remote --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/custatevec --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/helpers --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/mqpu/helpers/MQPUUtils.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/fermioniq --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/fermioniq/FermioniqBaseQPU.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/fermioniq/helpers --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/qpu.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/orca_qpu.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/OrcaRemoteRESTQPU.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/OrcaExecutor.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/orca/OrcaServerHelper.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/quantum_platform.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/common --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server/helpers --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server/helpers/server_impl --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest_server/helpers/server_impl/RestServer.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/quantinuum --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/anyon --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/oqc --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/braket --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/ionq --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/iqm --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/default/rest/helpers/infleqtion --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/QuantumExecutionQueue.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/quera --- Installing: /usr/local/cudaq/include/cudaq/platform/quera/QuEraBaseQPU.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform/quera/QuEraServerHelper.h --- Up-to-date: /usr/local/cudaq/include/cudaq/Todo.h --- Up-to-date: /usr/local/cudaq/include/cudaq/dynamics --- Installing: /usr/local/cudaq/include/cudaq/dynamics/helpers.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/manipulation.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/boson_operators.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/operator_leafs.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/matrix_operators.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/spin_operators.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/callback.h --- Installing: /usr/local/cudaq/include/cudaq/dynamics/templates.h --- Up-to-date: /usr/local/cudaq/include/cudaq/orca.h --- Up-to-date: /usr/local/cudaq/include/cudaq/domains --- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry --- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/uccsd.h --- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/MoleculePackageDriver.h --- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/molecule.h --- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry/hwe.h --- Up-to-date: /usr/local/cudaq/include/cudaq/domains/chemistry.h --- Up-to-date: /usr/local/cudaq/include/cudaq/platform.h --- Up-to-date: /usr/local/cudaq/include/cudaq/host_config.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis --- Installing: /usr/local/cudaq/include/cudaq/qis/pauli_word.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qudit.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qspan.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qreg.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qview.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qubit_qis.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/default --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/BasicExecutionManager.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/photonics --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/managers/photonics/photonics_qis.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qvector.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qkernel.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/modifiers.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/execution_manager.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/remote_state.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/qarray.h --- Up-to-date: /usr/local/cudaq/include/cudaq/qis/state.h --- Up-to-date: /usr/local/cudaq/include/cudaq/distributed --- Up-to-date: /usr/local/cudaq/include/cudaq/distributed/builtin --- Installing: /usr/local/cudaq/include/cudaq/distributed/distributed_capi.h --- Up-to-date: /usr/local/cudaq/include/cudaq/distributed/mpi_plugin.h --- Up-to-date: /usr/local/cudaq/include/cudaq/gradients.h --- Installing: /usr/local/cudaq/include/cudaq/schedule.h --- Installing: /usr/local/cudaq/include/cudaq/base_integrator.h --- Up-to-date: /usr/local/cudaq/include/cudaq/matrix.h --- Up-to-date: /usr/local/cudaq/include/cudaq/spin --- Up-to-date: /usr/local/cudaq/include/cudaq/kernels --- Up-to-date: /usr/local/cudaq/include/cudaq/kernels/givens_rotation.h --- Up-to-date: /usr/local/cudaq/include/cudaq/kernels/fermionic_swap.h --- Up-to-date: /usr/local/cudaq/include/cudaq/builder.h --- Installing: /usr/local/cudaq/include/cudaq/operators.h --- Installing: /usr/local/cudaq/include/cudaq/base_time_stepper.h --- Up-to-date: /usr/local/cudaq/include/cudaq/optimizers.h --- Up-to-date: /usr/local/cudaq/include/cudaq/photonics.h --- Up-to-date: /usr/local/cudaq/include/cudaq/simulators.h --- Up-to-date: /usr/local/cudaq/include/cudaq/utils --- Up-to-date: /usr/local/cudaq/include/cudaq/utils/cudaq_utils.h --- Installing: /usr/local/cudaq/include/cudaq/utils/tensor.h --- Up-to-date: /usr/local/cudaq/include/cudaq/utils/registry.h --- Up-to-date: /usr/local/cudaq/include/cudaq/remote_capabilities.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradient.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/vqe.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/nlopt --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/nlopt/nlopt.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/ensmallen --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizers/ensmallen/ensmallen.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/sample.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/optimizer.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/draw.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/broadcast.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/get_state.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/evolve.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients/forward_difference.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients/central_difference.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/gradients/parameter_shift.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/resource_estimation.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/observe.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithms/state.h --- Up-to-date: /usr/local/cudaq/include/cudaq/spin_op.h --- Up-to-date: /usr/local/cudaq/include/cudaq/algorithm.h --- Up-to-date: /usr/local/cudaq/include/cudaq/builder --- Up-to-date: /usr/local/cudaq/include/cudaq/builder/kernels.h --- Up-to-date: /usr/local/cudaq/include/cudaq/builder/QuakeValue.h --- Up-to-date: /usr/local/cudaq/include/cudaq/builder/kernel_builder.h --- Up-to-date: /usr/local/cudaq/include/cudaq/target_control.h --- Up-to-date: /usr/local/cudaq/include/cudaq/concepts.h --- Up-to-date: /usr/local/cudaq/include/common --- Installing: /usr/local/cudaq/include/common/NoiseModel.h --- Up-to-date: /usr/local/cudaq/include/common/EigenSparse.h --- Up-to-date: /usr/local/cudaq/include/common/JIT.h --- Up-to-date: /usr/local/cudaq/include/common/JsonConvert.h --- Up-to-date: /usr/local/cudaq/include/common/BaseRemoteSimulatorQPU.h --- Up-to-date: /usr/local/cudaq/include/common/NvqcConfig.h --- Installing: /usr/local/cudaq/include/common/BaseRemoteRESTQPU.h --- Up-to-date: /usr/local/cudaq/include/common/MeasureCounts.h --- Up-to-date: /usr/local/cudaq/include/common/Executor.h --- Up-to-date: /usr/local/cudaq/include/common/CustomOp.h --- Up-to-date: /usr/local/cudaq/include/common/ServerHelper.h --- Up-to-date: /usr/local/cudaq/include/common/Logger.h --- Up-to-date: /usr/local/cudaq/include/common/ObserveResult.h --- Up-to-date: /usr/local/cudaq/include/common/ThunkInterface.h --- Installing: /usr/local/cudaq/include/common/BraketExecutor.h --- Up-to-date: /usr/local/cudaq/include/common/ArgumentWrapper.h --- Up-to-date: /usr/local/cudaq/include/common/ArgumentConversion.h --- Up-to-date: /usr/local/cudaq/include/common/Trace.h --- Up-to-date: /usr/local/cudaq/include/common/QuditIdTracker.h --- Up-to-date: /usr/local/cudaq/include/common/BaseRestRemoteClient.h --- Up-to-date: /usr/local/cudaq/include/common/EigenDense.h --- Up-to-date: /usr/local/cudaq/include/common/FmtCore.h --- Up-to-date: /usr/local/cudaq/include/common/PluginUtils.h --- Up-to-date: /usr/local/cudaq/include/common/EvolveResult.h --- Up-to-date: /usr/local/cudaq/include/common/Future.h --- Up-to-date: /usr/local/cudaq/include/common/RemoteKernelExecutor.h --- Installing: /usr/local/cudaq/include/common/RuntimeMLIRCommonImpl.h --- Up-to-date: /usr/local/cudaq/include/common/GPUInfo.h --- Up-to-date: /usr/local/cudaq/include/common/RuntimeMLIR.h --- Up-to-date: /usr/local/cudaq/include/common/Registry.h --- Up-to-date: /usr/local/cudaq/include/common/Resources.h --- Up-to-date: /usr/local/cudaq/include/common/KernelWrapper.h --- Up-to-date: /usr/local/cudaq/include/common/AnalogHamiltonian.h --- Up-to-date: /usr/local/cudaq/include/common/SerializedCodeExecutionContext.h --- Up-to-date: /usr/local/cudaq/include/common/Environment.h --- Installing: /usr/local/cudaq/include/common/BraketServerHelper.h --- Up-to-date: /usr/local/cudaq/include/common/UnzipUtils.h --- Up-to-date: /usr/local/cudaq/include/common/ExecutionContext.h --- Up-to-date: /usr/local/cudaq/include/common/SimulationState.h --- Up-to-date: /usr/local/cudaq/include/common/RestClient.h --- Up-to-date: /usr/local/cudaq/include/common/Timing.h --- Up-to-date: /usr/local/cudaq/include/nvqir/CircuitSimulator.h --- Up-to-date: /usr/local/cudaq/include/nvqir/QIRTypes.h --- Up-to-date: /usr/local/cudaq/include/nvqir/Gates.h --- Installing: /usr/local/cudaq/include/cudaq.h --- Installing: /usr/local/cudaq/bin/cudaq-opt --- Installing: /usr/local/cudaq/bin/cudaq-translate --- Installing: /usr/local/cudaq/bin/cudaq-lsp-server --- Installing: /usr/local/cudaq/bin/cudaq-quake --- Up-to-date: /usr/local/cudaq/bin/fixup-linkage --- Up-to-date: /usr/local/cudaq/include/nvqpp/nvqpp_config.h --- Installing: /usr/local/cudaq/bin/nvq++ --- Up-to-date: /usr/local/cudaq/targets/backendConfig.cpp --- Up-to-date: /usr/local/cudaq/bin/cudaq-target-conf --- Installing: /usr/local/cudaq/bin/cudaq-qpud --- Up-to-date: /usr/local/cudaq/bin/cudaq-qpud.py --- Up-to-date: /usr/local/cudaq/bin/nvqc_proxy.py --- Up-to-date: /usr/local/cudaq/bin/json_request_runner.py --- Installing: /usr/local/cudaq/lib/libcudaq-py-utils.so --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.quake/dialects/quake.py --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.quake.ops_gen/dialects/_quake_ops_gen.py --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.cc/dialects/cc.py --- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.cc.ops_gen/dialects/_cc_ops_gen.py --- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/CUDAQuantumExtension.cpp --- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_ExecutionContext.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_NoiseModel.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_EvolveResult.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_ObserveResult.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_SampleResult.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_CustomOpRegistry.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/common/py_AnalogHamiltonian.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_draw.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_observe_async.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_optimizer.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_sample_async.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_state.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_evolve.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_translate.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_utils.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/algorithms/py_vqe.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/platform/JITExecutionCache.cpp --- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/platform/py_alt_launch_kernel.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/qis/py_execution_manager.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/qis/py_qubit_qis.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/spin/py_matrix.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/spin/py_spin_op.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/target/py_runtime_target.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/cudaq/target/py_testing_utils.cpp --- Installing: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/mlir/py_register_dialects.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyRemoteRESTQPU.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyFermioniqRESTQPU.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyQuEraRemoteRESTQPU.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyRemoteSimulatorQPU.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../runtime/utils/PyRestRemoteClient.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../utils/LinkedLibraryHolder.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/common/ArgumentConversion.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/common/QuantumExecutionQueue.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/default/rest_server/RemoteRuntimeClient.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaExecutor.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaQPU.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaRemoteRESTQPU.cpp --- Up-to-date: /usr/local/cudaq/src/python/CUDAQuantumPythonSources.Extension/../../runtime/cudaq/platform/orca/OrcaServerHelper.cpp --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/libCUDAQuantumPythonCAPI.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/libCUDAQuantumPythonCAPI.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/quake.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_quake_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/cc.py --- Installing: /usr/local/cudaq/cudaq/mlir/dialects/_cc_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_quakeDialects.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_quakeDialects.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN:$ORIGIN/../../../lib:$ORIGIN/../../../lib/plugins" --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirRegisterEverything.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirRegisterEverything.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/async_dialect/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/async_dialect/passes/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_async_dialect_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirAsyncPasses.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirAsyncPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/bufferization.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_bufferization_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_bufferization_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/builtin.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_builtin_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_builtin_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/complex.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_complex_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/cf.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_cf_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/func.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_func_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_func_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/gpu/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/gpu/passes/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_gpu_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirGPUPasses.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirGPUPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_linalg_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/dump_oplib.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/affine.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/comprehension.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/config.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/dsl.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/emitter.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/scalar_expr.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/types.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/lang/yaml_helper.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/ops/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/opdsl/ops/core_named_ops.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/linalg/passes/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_linalg_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsLinalg.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsLinalg.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirLinalgPasses.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirLinalgPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_transform_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/transform/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/dialects/transform/__init__.pyi --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_transform_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsTransform.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsTransform.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_loop_transform_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/transform/loop.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_loop_transform_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_structured_transform_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/transform/structured.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_structured_transform_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/math.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_math_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/arith.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_arith_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_arith_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/memref.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_memref_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_memref_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/ml_program.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_ml_program_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_ml_program_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/quant.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/dialects/quant.pyi --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsQuant.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsQuant.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/pdl.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_pdl_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/dialects/pdl.pyi --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_pdl_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsPDL.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsPDL.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/scf.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_scf_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_scf_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/shape.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_shape_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/sparse_tensor.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_sparse_tensor_ops_gen.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsSparseTensor.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirDialectsSparseTensor.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirSparseTensorPasses.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirSparseTensorPasses.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/tensor.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_tensor_ops_ext.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_tensor_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/tosa.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_tosa_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/vector.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_vector_ops_gen.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/ir.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/passmanager.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/dialects/_ods_common.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/__init__.pyi --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/ir.pyi --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir/passmanager.pyi --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlir.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Up-to-date: /usr/local/cudaq/cudaq/mlir/execution_engine.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.pyi --- Up-to-date: /usr/local/cudaq/cudaq/mlir/runtime/__init__.py --- Up-to-date: /usr/local/cudaq/cudaq/mlir/runtime/np_to_memref.py --- Installing: /usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.cpython-310-x86_64-linux-gnu.so --- Set runtime path of "/usr/local/cudaq/cudaq/mlir/_mlir_libs/_mlirExecutionEngine.cpython-310-x86_64-linux-gnu.so" to "$ORIGIN" --- Installing: /usr/local/cudaq/lib/plugins/libcudaq-pyscf.so --- Up-to-date: /usr/local/cudaq/./cudaq --- Up-to-date: /usr/local/cudaq/./cudaq/display --- Up-to-date: /usr/local/cudaq/./cudaq/display/display_trace.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator --- Installing: /usr/local/cudaq/./cudaq/operator/integrator.py --- Installing: /usr/local/cudaq/./cudaq/operator/helpers.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/CMakeLists.txt --- Installing: /usr/local/cudaq/./cudaq/operator/evolution.py --- Installing: /usr/local/cudaq/./cudaq/operator/cudm_solver.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/definitions.py --- Installing: /usr/local/cudaq/./cudaq/operator/manipulation.py --- Installing: /usr/local/cudaq/./cudaq/operator/cudm_state.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators --- Installing: /usr/local/cudaq/./cudaq/operator/integrators/builtin_integrators.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators/cuda_torchdiffeq_integrator.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/integrators/scipy_integrators.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/expressions.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/dynamics.yml --- Up-to-date: /usr/local/cudaq/./cudaq/operator/cudm_helpers.py --- Installing: /usr/local/cudaq/./cudaq/operator/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/operator/schedule.py --- Up-to-date: /usr/local/cudaq/./cudaq/domains --- Up-to-date: /usr/local/cudaq/./cudaq/domains/chemistry --- Up-to-date: /usr/local/cudaq/./cudaq/domains/chemistry/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/domains/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/qis --- Up-to-date: /usr/local/cudaq/./cudaq/qis/qis.py --- Up-to-date: /usr/local/cudaq/./cudaq/qis/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernels --- Up-to-date: /usr/local/cudaq/./cudaq/kernels/hwe.py --- Installing: /usr/local/cudaq/./cudaq/kernels/uccsd.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernels/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/lib --- Up-to-date: /usr/local/cudaq/./cudaq/lib/composite_operations --- Up-to-date: /usr/local/cudaq/./cudaq/lib/composite_operations/fswap.py --- Up-to-date: /usr/local/cudaq/./cudaq/lib/composite_operations/givens.py --- Up-to-date: /usr/local/cudaq/./cudaq/lib/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/util --- Up-to-date: /usr/local/cudaq/./cudaq/util/timing_helper.py --- Up-to-date: /usr/local/cudaq/./cudaq/handlers --- Up-to-date: /usr/local/cudaq/./cudaq/handlers/photonics_kernel.py --- Up-to-date: /usr/local/cudaq/./cudaq/handlers/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/visualization --- Up-to-date: /usr/local/cudaq/./cudaq/visualization/bloch_visualize.py --- Up-to-date: /usr/local/cudaq/./cudaq/visualization/bloch_visualize_err.py --- Up-to-date: /usr/local/cudaq/./cudaq/mlir --- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects --- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/CCOps.td --- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/QuakeOps.td --- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/cc.py --- Up-to-date: /usr/local/cudaq/./cudaq/mlir/dialects/quake.py --- Up-to-date: /usr/local/cudaq/./cudaq/_packages.py --- Up-to-date: /usr/local/cudaq/./cudaq/dbg --- Up-to-date: /usr/local/cudaq/./cudaq/dbg/ast.py --- Up-to-date: /usr/local/cudaq/./cudaq/dbg/__init__.py --- Up-to-date: /usr/local/cudaq/./cudaq/runtime --- Up-to-date: /usr/local/cudaq/./cudaq/runtime/utils.py --- Up-to-date: /usr/local/cudaq/./cudaq/runtime/state.py --- Installing: /usr/local/cudaq/./cudaq/runtime/observe.py --- Up-to-date: /usr/local/cudaq/./cudaq/runtime/sample.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernel --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/quake_value.py --- Installing: /usr/local/cudaq/./cudaq/kernel/ast_bridge.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/utils.py --- Installing: /usr/local/cudaq/./cudaq/kernel/kernel_decorator.py --- Installing: /usr/local/cudaq/./cudaq/kernel/kernel_builder.py --- Installing: /usr/local/cudaq/./cudaq/kernel/analysis.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/register_op.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/common --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/common/fermionic_swap.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/common/givens.py --- Up-to-date: /usr/local/cudaq/./cudaq/kernel/captured_data.py --- Up-to-date: /usr/local/cudaq/./cudaq/__init__.py --- Installing: /usr/local/cudaq/cudaq/_metadata.py --- Up-to-date: /usr/local/cudaq/include/cudaq/python/PythonCppInterop.h --- Installing: /usr/local/cudaq/lib/libcudaq-python-interop.so --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPythonInteropTargets.cmake --- Up-to-date: /usr/local/cudaq/lib/cmake/cudaq/CUDAQPythonInteropTargets-release.cmake --- Up-to-date: /usr/local/cudaq/targets/dynamics.yml --- Up-to-date: /usr/local/cudaq/lib/CustomPassPlugin.so --- Installing: /usr/local/cudaq/lib/libcudaq-em-qudit.so --- Set runtime path of "/usr/local/cudaq/lib/libcudaq-em-qudit.so" to "$ORIGIN:$ORIGIN/lib:$ORIGIN/lib/plugins:$ORIGIN/../lib:$ORIGIN/../lib/plugins" -Installed CUDA-Q in directory: /usr/local/cudaq From 940cdc48bb41bdadc4f61b6a05a4acf5bbbef8ed Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 20 Feb 2025 06:07:30 +0000 Subject: [PATCH 303/311] [Cleanup] Tidy up the code Signed-off-by: Thien Nguyen --- runtime/cudaq/algorithms/evolve.h | 15 +- runtime/cudaq/dynamics_integrators.h | 2 +- runtime/cudaq/evolution.h | 4 +- runtime/nvqir/cudensitymat/CMakeLists.txt | 1 + .../cudensitymat/CuDensityMatContext.cpp | 2 + .../nvqir/cudensitymat/CuDensityMatContext.h | 5 +- .../cudensitymat/CuDensityMatOpConverter.cpp | 668 ++++++++++++++++++ .../cudensitymat/CuDensityMatOpConverter.h | 96 +++ runtime/nvqir/cudensitymat/cudm_evolution.cpp | 13 +- .../cudensitymat/runge_kutta_integrator.cpp | 17 +- unittests/dynamics/test_evolve_single.cpp | 14 +- 11 files changed, 794 insertions(+), 43 deletions(-) create mode 100644 runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp create mode 100644 runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h diff --git a/runtime/cudaq/algorithms/evolve.h b/runtime/cudaq/algorithms/evolve.h index 46b5dfd762..f293d17b67 100644 --- a/runtime/cudaq/algorithms/evolve.h +++ b/runtime/cudaq/algorithms/evolve.h @@ -169,21 +169,8 @@ evolve(const cudaq::operator_sum &hamiltonian, bool store_intermediate_results = false, std::optional shots_count = std::nullopt) { #if defined(CUDAQ_DYNAMICS_TARGET) - - std::vector *> collapseOpsPtr; - for (const auto &cOp : collapse_operators) { - collapseOpsPtr.emplace_back( - const_cast *>(&cOp)); - } - std::vector *> observeOpsPtr; - for (const auto &obsOp : observables) { - observeOpsPtr.emplace_back( - const_cast *>(&obsOp)); - } - // FIXME: change signature of `evolve_single` so that we don't need to - // create the list of pointers. return evolve_single(hamiltonian, dimensions, schedule, initial_state, - *integrator, collapseOpsPtr, observeOpsPtr, + *integrator, collapse_operators, observables, store_intermediate_results); #else diff --git a/runtime/cudaq/dynamics_integrators.h b/runtime/cudaq/dynamics_integrators.h index 0d6d07771b..c606f16472 100644 --- a/runtime/cudaq/dynamics_integrators.h +++ b/runtime/cudaq/dynamics_integrators.h @@ -16,7 +16,7 @@ namespace cudaq { struct SystemDynamics { operator_sum *hamiltonian = nullptr; - std::vector *> collapseOps; + std::vector> collapseOps; std::vector modeExtents; std::unordered_map> parameters; }; diff --git a/runtime/cudaq/evolution.h b/runtime/cudaq/evolution.h index fcc3ad7b4b..54d1042086 100644 --- a/runtime/cudaq/evolution.h +++ b/runtime/cudaq/evolution.h @@ -25,9 +25,9 @@ evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, const state &initial_state, BaseIntegrator &integrator, - const std::vector *> + const std::vector> &collapse_operators = {}, - const std::vector *> &observables = {}, + const std::vector> &observables = {}, bool store_intermediate_results = false, std::optional shots_count = std::nullopt); // class Evolution { diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 51eba42425..6ab6216ff8 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -34,6 +34,7 @@ add_library(${LIBRARY_NAME} SHARED cudm_evolution.cpp CuDensityMatState.cpp CuDensityMatContext.cpp + CuDensityMatOpConverter.cpp ) message("CUDAToolkit_INCLUDE_DIRS = ${CUDAToolkit_INCLUDE_DIRS}") diff --git a/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp b/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp index 5e5c14313f..4da9e8ce86 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatContext.cpp @@ -64,9 +64,11 @@ std::size_t Context::getRecommendedWorkSpaceLimit() { Context::Context(int deviceId) : m_deviceId(deviceId) { HANDLE_CUDA_ERROR(cudaSetDevice(deviceId)); HANDLE_CUDM_ERROR(cudensitymatCreate(&m_cudmHandle)); + m_opConverter = std::make_unique(m_cudmHandle); } Context::~Context() { + m_opConverter.reset(); cudensitymatDestroy(m_cudmHandle); if (m_scratchSpaceSizeBytes > 0) cudaFree(m_scratchSpace); diff --git a/runtime/nvqir/cudensitymat/CuDensityMatContext.h b/runtime/nvqir/cudensitymat/CuDensityMatContext.h index 6876864d95..c4fdf35903 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatContext.h +++ b/runtime/nvqir/cudensitymat/CuDensityMatContext.h @@ -7,9 +7,9 @@ ******************************************************************************/ #pragma once +#include "CuDensityMatOpConverter.h" #include #include - namespace cudaq { namespace dynamics { class Context { @@ -19,7 +19,7 @@ class Context { ~Context(); cudensitymatHandle_t getHandle() const { return m_cudmHandle; } - + OpConverter &getOpConverter() { return *m_opConverter; } static Context *getCurrentContext(); void *getScratchSpace(std::size_t minSizeBytes); static std::size_t getRecommendedWorkSpaceLimit(); @@ -27,6 +27,7 @@ class Context { private: Context(int deviceId); cudensitymatHandle_t m_cudmHandle; + std::unique_ptr m_opConverter; int m_deviceId; void *m_scratchSpace{nullptr}; std::size_t m_scratchSpaceSizeBytes{0}; diff --git a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp new file mode 100644 index 0000000000..87e9f03c32 --- /dev/null +++ b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp @@ -0,0 +1,668 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include "CuDensityMatOpConverter.h" +#include "common/Logger.h" +#include "cudm_error_handling.h" +#include + +namespace { +std::vector getSubspaceExtents(const std::vector &modeExtents, + const std::vector °rees) { + std::vector subspaceExtents; + + for (int degree : degrees) { + if (degree >= modeExtents.size()) + throw std::out_of_range("Degree exceeds modeExtents size."); + + subspaceExtents.push_back(modeExtents[degree]); + } + + return subspaceExtents; +} + +std::unordered_map +convertDimensions(const std::vector &modeExtents) { + + std::unordered_map dimensions; + for (size_t i = 0; i < modeExtents.size(); ++i) + dimensions[static_cast(i)] = static_cast(modeExtents[i]); + + return dimensions; +} + +// Function to flatten a matrix into a 1D array (column major) +std::vector> +flattenMatrixColumnMajor(const cudaq::matrix_2 &matrix) { + std::vector> flatMatrix; + flatMatrix.reserve(matrix.get_size()); + for (size_t col = 0; col < matrix.get_columns(); col++) { + for (size_t row = 0; row < matrix.get_rows(); row++) { + flatMatrix.push_back(matrix[{row, col}]); + } + } + + return flatMatrix; +} +void *createArrayGpu(const std::vector> &cpuArray) { + void *gpuArray{nullptr}; + const std::size_t array_size = cpuArray.size() * sizeof(std::complex); + if (array_size > 0) { + HANDLE_CUDA_ERROR(cudaMalloc(&gpuArray, array_size)); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuArray, + static_cast(cpuArray.data()), + array_size, cudaMemcpyHostToDevice)); + } + return gpuArray; +} + +// Function to destroy a previously created array copy in GPU memory +void destroyArrayGpu(void *gpuArray) { + if (gpuArray) + HANDLE_CUDA_ERROR(cudaFree(gpuArray)); +} + +cudaq::product_operator +computeDagger(const cudaq::matrix_operator &op) { + const std::string daggerOpName = op.to_string(false) + "_dagger"; + try { + auto func = [op](const std::vector &dimensions, + const std::unordered_map> + ¶ms) { + std::unordered_map dims; + if (dimensions.size() != op.degrees().size()) + throw std::runtime_error("Dimension mismatched"); + + for (int i = 0; i < dimensions.size(); ++i) { + dims[op.degrees()[i]] = dimensions[i]; + } + auto originalMat = op.to_matrix(dims, params); + return cudaq::matrix_2::adjoint(originalMat); + }; + cudaq::matrix_operator::define(daggerOpName, {-1}, std::move(func)); + } catch (...) { + // Nothing, this has been define + } + return cudaq::matrix_operator::instantiate(daggerOpName, op.degrees()); +} + +cudaq::scalar_operator computeDagger(const cudaq::scalar_operator &scalar) { + if (scalar.is_constant()) { + return cudaq::scalar_operator(std::conj(scalar.evaluate())); + } else { + return cudaq::scalar_operator( + [scalar]( + const std::unordered_map> ¶ms) + -> std::complex { + return std::conj(scalar.evaluate(params)); + }); + } +} + +cudaq::product_operator computeDagger( + const cudaq::product_operator &productOp) { + std::vector> daggerOps; + for (const auto &component : productOp.get_terms()) { + if (const auto *elemOp = + dynamic_cast(&component)) { + daggerOps.emplace_back(computeDagger(*elemOp)); + } else { + throw std::runtime_error("Unhandled type!"); + } + } + std::reverse(daggerOps.begin(), daggerOps.end()); + + if (daggerOps.empty()) { + throw std::runtime_error("Empty product operator"); + } + cudaq::product_operator daggerProduct = daggerOps[0]; + for (std::size_t i = 1; i < daggerOps.size(); ++i) { + daggerProduct *= daggerOps[i]; + } + daggerProduct *= computeDagger(productOp.get_coefficient()); + return daggerProduct; +} +} // namespace + +cudensitymatOperator_t cudaq::dynamics::OpConverter::constructLiouvillian( + const operator_sum &ham, + const std::vector> &collapseOperators, + const std::vector &modeExtents, + const std::unordered_map> ¶meters, + bool isMasterEquation) { + if (!isMasterEquation && collapseOperators.empty()) { + cudaq::info("Construct state vector Liouvillian"); + auto liouvillian = ham * std::complex(0.0, -1.0); + return convertToCudensitymatOperator(parameters, liouvillian, modeExtents); + } else { + cudaq::info("Construct density matrix Liouvillian"); + cudensitymatOperator_t liouvillian; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + m_handle, static_cast(modeExtents.size()), modeExtents.data(), + &liouvillian)); + // Append an operator term to the operator (super-operator) + // Handle the Hamiltonian + const std::map> sortedParameters( + parameters.begin(), parameters.end()); + auto ks = std::views::keys(sortedParameters); + const std::vector keys{ks.begin(), ks.end()}; + for (auto &[coeff, term] : + convertToCudensitymat(ham, parameters, modeExtents)) { + cudensitymatWrappedScalarCallback_t wrappedCallback = {nullptr, nullptr}; + if (coeff.is_constant()) { + const auto coeffVal = coeff.evaluate(); + const auto leftCoeff = std::complex(0.0, -1.0) * coeffVal; + // -i constant (left multiplication) + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, liouvillian, term, 0, + make_cuDoubleComplex(leftCoeff.real(), leftCoeff.imag()), + wrappedCallback)); + + // +i constant (right multiplication, i.e., dual) + const auto rightCoeff = std::complex(0.0, 1.0) * coeffVal; + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, liouvillian, term, 1, + make_cuDoubleComplex(rightCoeff.real(), rightCoeff.imag()), + wrappedCallback)); + } else { + wrappedCallback = wrapScalarCallback(coeff, keys); + // -i constant (left multiplication) + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, liouvillian, term, 0, make_cuDoubleComplex(0.0, -1.0), + wrappedCallback)); + + // +i constant (right multiplication, i.e., dual) + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, liouvillian, term, 1, make_cuDoubleComplex(0.0, 1.0), + wrappedCallback)); + } + } + + // Handle collapsed operators + for (auto &collapseOperator : collapseOperators) { + for (auto &[coeff, term] : + computeLindbladTerms(collapseOperator, modeExtents, parameters)) { + cudensitymatWrappedScalarCallback_t wrappedCallback = {nullptr, + nullptr}; + if (coeff.is_constant()) { + const auto coeffVal = coeff.evaluate(); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, liouvillian, term, 0, + make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), + wrappedCallback)); + } else { + wrappedCallback = wrapScalarCallback(coeff, keys); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, liouvillian, term, 0, make_cuDoubleComplex(1.0, 0.0), + wrappedCallback)); + } + } + } + + return liouvillian; + } +} + +cudaq::dynamics::OpConverter::~OpConverter() { + for (auto term : m_operatorTerms) + cudensitymatDestroyOperatorTerm(term); + + for (auto op : m_elementaryOperators) + cudensitymatDestroyElementaryOperator(op); + + for (auto *buffer : m_deviceBuffers) + cudaFree(buffer); +} + +cudensitymatElementaryOperator_t +cudaq::dynamics::OpConverter::createElementaryOperator( + const cudaq::matrix_operator &elemOp, + const std::unordered_map> ¶meters, + const std::vector &modeExtents) { + auto subspaceExtents = getSubspaceExtents(modeExtents, elemOp.degrees()); + std::unordered_map dimensions = convertDimensions(modeExtents); + cudensitymatWrappedTensorCallback_t wrappedTensorCallback = {nullptr, + nullptr}; + // This is a callback + if (!parameters.empty()) { + const std::map> sortedParameters( + parameters.begin(), parameters.end()); + auto ks = std::views::keys(sortedParameters); + const std::vector keys{ks.begin(), ks.end()}; + wrappedTensorCallback = wrapTensorCallback(elemOp, keys); + } + + auto flatMatrix = + flattenMatrixColumnMajor(elemOp.to_matrix(dimensions, parameters)); + + if (flatMatrix.empty()) { + throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); + } + + if (subspaceExtents.empty()) { + throw std::invalid_argument("subspaceExtents cannot be empty."); + } + + auto *elementaryMat_d = createArrayGpu(flatMatrix); + cudensitymatElementaryOperator_t cudmElemOp = nullptr; + + HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( + m_handle, static_cast(subspaceExtents.size()), + subspaceExtents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, + CUDA_C_64F, elementaryMat_d, wrappedTensorCallback, &cudmElemOp)); + + if (!cudmElemOp) { + std::cerr << "[ERROR] cudmElemOp is NULL in createElementaryOperator !" + << std::endl; + destroyArrayGpu(elementaryMat_d); + throw std::runtime_error("Failed to create elementary operator."); + } + m_elementaryOperators.emplace(cudmElemOp); + m_deviceBuffers.emplace(elementaryMat_d); + return cudmElemOp; +} + +cudensitymatOperatorTerm_t +cudaq::dynamics::OpConverter::createProductOperatorTerm( + const std::vector &elemOps, + const std::vector &modeExtents, + const std::vector> °rees, + const std::vector> &dualModalities) { + + cudensitymatOperatorTerm_t term; + HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( + m_handle, static_cast(modeExtents.size()), modeExtents.data(), + &term)); + m_operatorTerms.emplace(term); + if (degrees.empty()) { + throw std::invalid_argument("Degrees vector cannot be empty."); + } + + if (elemOps.empty()) { + throw std::invalid_argument("elemOps cannot be null."); + } + + if (degrees.size() != elemOps.size()) { + throw std::invalid_argument("elemOps and degrees must have the same size."); + } + + const bool hasDualModalities = !dualModalities.empty(); + + if (hasDualModalities && degrees.size() != dualModalities.size()) { + throw std::invalid_argument( + "degrees and dualModalities must have the same size."); + } + + std::vector allDegrees; + std::vector allModeActionDuality; + for (size_t i = 0; i < degrees.size(); i++) { + const auto &sub_degrees = degrees[i]; + const auto &modalities = hasDualModalities + ? dualModalities[i] + : std::vector(sub_degrees.size(), 0); + + if (sub_degrees.size() != modalities.size()) { + throw std::runtime_error( + "Mismatch between degrees and modalities sizes."); + } + if (sub_degrees.size() != 1) { + throw std::runtime_error( + "Elementary operator must act on a single degree."); + } + + for (size_t j = 0; j < sub_degrees.size(); j++) { + int degree = sub_degrees[j]; + int modality = modalities[j]; + + if (sub_degrees[i] < 0) { + throw std::out_of_range("Degree cannot be negative!"); + } + allDegrees.emplace_back(degree); + allModeActionDuality.emplace_back(modality); + } + } + + assert(elemOps.size() == degrees.size()); + HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( + m_handle, term, static_cast(elemOps.size()), elemOps.data(), + allDegrees.data(), allModeActionDuality.data(), + make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); + return term; +} + +cudensitymatOperator_t +cudaq::dynamics::OpConverter::convertToCudensitymatOperator( + const std::unordered_map> ¶meters, + const operator_sum &op, + const std::vector &modeExtents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + + cudensitymatOperator_t cudmOperator; + HANDLE_CUDM_ERROR(cudensitymatCreateOperator( + m_handle, static_cast(modeExtents.size()), modeExtents.data(), + &cudmOperator)); + + const std::map> sortedParameters( + parameters.begin(), parameters.end()); + auto ks = std::views::keys(sortedParameters); + const std::vector keys{ks.begin(), ks.end()}; + for (auto &[coeff, term] : + convertToCudensitymat(op, parameters, modeExtents)) { + cudensitymatWrappedScalarCallback_t wrappedCallback = {nullptr, nullptr}; + + if (coeff.is_constant()) { + const auto coeffVal = coeff.evaluate(); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, cudmOperator, term, 0, + make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), + wrappedCallback)); + } else { + wrappedCallback = wrapScalarCallback(coeff, keys); + HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( + m_handle, cudmOperator, term, 0, make_cuDoubleComplex(1.0, 0.0), + wrappedCallback)); + } + } + + return cudmOperator; +} + +std::vector> +cudaq::dynamics::OpConverter::convertToCudensitymat( + const operator_sum &op, + const std::unordered_map> ¶meters, + const std::vector &modeExtents) { + if (op.get_terms().empty()) { + throw std::invalid_argument("Operator sum cannot be empty."); + } + + std::vector> + result; + + for (const auto &productOp : op.get_terms()) { + std::vector elemOps; + std::vector> allDegrees; + for (const auto &component : productOp.get_terms()) { + // No need to check type + // just call to_matrix on it + if (const auto *elemOp = + dynamic_cast(&component)) { + auto cudmElemOp = + createElementaryOperator(*elemOp, parameters, modeExtents); + elemOps.emplace_back(cudmElemOp); + allDegrees.emplace_back(elemOp->degrees()); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + result.emplace_back(std::make_pair( + productOp.get_coefficient(), + createProductOperatorTerm(elemOps, modeExtents, allDegrees, {}))); + } + return result; +} + +std::vector> +cudaq::dynamics::OpConverter::computeLindbladTerms( + const operator_sum &collapseOp, + const std::vector &modeExtents, + const std::unordered_map> ¶meters) { + std::vector> + lindbladTerms; + for (const product_operator &l_op : collapseOp.get_terms()) { + for (const product_operator &r_op : + collapseOp.get_terms()) { + scalar_operator coeff = + l_op.get_coefficient() * computeDagger(r_op.get_coefficient()); + auto ldag = computeDagger(r_op); + { + // L * rho * L_dag + std::vector elemOps; + std::vector> allDegrees; + std::vector> all_action_dual_modalities; + + for (const auto &component : l_op.get_terms()) { + if (const auto *elemOp = + dynamic_cast(&component)) { + auto cudmElemOp = + createElementaryOperator(*elemOp, parameters, modeExtents); + elemOps.emplace_back(cudmElemOp); + allDegrees.emplace_back(elemOp->degrees()); + all_action_dual_modalities.emplace_back( + std::vector(elemOp->degrees().size(), 0)); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + + for (const auto &component : ldag.get_terms()) { + if (const auto *elemOp = + dynamic_cast(&component)) { + auto cudmElemOp = + createElementaryOperator(*elemOp, parameters, modeExtents); + elemOps.emplace_back(cudmElemOp); + allDegrees.emplace_back(elemOp->degrees()); + all_action_dual_modalities.emplace_back( + std::vector(elemOp->degrees().size(), 1)); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + + cudensitymatOperatorTerm_t D1_term = createProductOperatorTerm( + elemOps, modeExtents, allDegrees, all_action_dual_modalities); + lindbladTerms.emplace_back(std::make_pair(coeff, D1_term)); + } + + product_operator L_daggerTimesL = -0.5 * ldag * l_op; + { + std::vector elemOps; + std::vector> allDegrees; + std::vector> all_action_dual_modalities_left; + std::vector> all_action_dual_modalities_right; + for (const auto &component : L_daggerTimesL.get_terms()) { + if (const auto *elemOp = + dynamic_cast(&component)) { + auto cudmElemOp = + createElementaryOperator(*elemOp, parameters, modeExtents); + elemOps.emplace_back(cudmElemOp); + allDegrees.emplace_back(elemOp->degrees()); + all_action_dual_modalities_left.emplace_back( + std::vector(elemOp->degrees().size(), 0)); + all_action_dual_modalities_right.emplace_back( + std::vector(elemOp->degrees().size(), 1)); + } else { + // Catch anything that we don't know + throw std::runtime_error("Unhandled type!"); + } + } + { + + // For left side, we need to reverse the order + std::vector d2Ops(elemOps); + std::reverse(d2Ops.begin(), d2Ops.end()); + std::vector> d2Degrees(allDegrees); + std::reverse(d2Degrees.begin(), d2Degrees.end()); + cudensitymatOperatorTerm_t D2_term = createProductOperatorTerm( + d2Ops, modeExtents, d2Degrees, all_action_dual_modalities_left); + lindbladTerms.emplace_back( + std::make_pair(L_daggerTimesL.get_coefficient(), D2_term)); + } + { + cudensitymatOperatorTerm_t D3_term = + createProductOperatorTerm(elemOps, modeExtents, allDegrees, + all_action_dual_modalities_right); + lindbladTerms.emplace_back( + std::make_pair(L_daggerTimesL.get_coefficient(), D3_term)); + } + } + } + } + return lindbladTerms; +} + +cudensitymatWrappedScalarCallback_t +cudaq::dynamics::OpConverter::wrapScalarCallback( + const scalar_operator &scalarOp, + const std::vector ¶mNames) { + if (scalarOp.is_constant()) { + throw std::runtime_error( + "scalar_operator does not have a valid generator function."); + } + + m_scalarCallbacks.push_back(ScalarCallBackContext(scalarOp, paramNames)); + ScalarCallBackContext *storedCallbackContext = &m_scalarCallbacks.back(); + using WrapperFuncType = + int32_t (*)(cudensitymatScalarCallback_t, double, int32_t, const double[], + cudaDataType_t, void *); + + auto wrapper = [](cudensitymatScalarCallback_t callback, double time, + int32_t numParams, const double params[], + cudaDataType_t dataType, void *scalarStorage) -> int32_t { + try { + ScalarCallBackContext *context = + reinterpret_cast(callback); + scalar_operator &storedOp = context->scalarOp; + if (numParams != 2 * context->paramNames.size()) + throw std::runtime_error( + fmt::format("[Internal Error] Invalid number of callback " + "parameters encountered. Expected {} double params " + "representing {} complex values but received {}.", + 2 * context->paramNames.size(), + context->paramNames.size(), numParams)); + + std::unordered_map> param_map; + for (size_t i = 0; i < context->paramNames.size(); ++i) { + param_map[context->paramNames[i]] = + std::complex(params[2 * i], params[2 * i + 1]); + cudaq::debug("Callback param name {}, value {}", context->paramNames[i], + param_map[context->paramNames[i]]); + } + + std::complex result = storedOp.evaluate(param_map); + cudaq::debug("Scalar callback evaluated result = {}", result); + auto *tdCoef = static_cast *>(scalarStorage); + *tdCoef = result; + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in scalar callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedScalarCallback_t wrappedCallback; + wrappedCallback.callback = + reinterpret_cast(storedCallbackContext); + wrappedCallback.wrapper = + reinterpret_cast(static_cast(wrapper)); + return wrappedCallback; +} + +cudensitymatWrappedTensorCallback_t +cudaq::dynamics::OpConverter::wrapTensorCallback( + const matrix_operator &matrixOp, + const std::vector ¶mNames) { + m_tensorCallbacks.push_back(TensorCallBackContext(matrixOp, paramNames)); + TensorCallBackContext *storedCallbackContext = &m_tensorCallbacks.back(); + using WrapperFuncType = int32_t (*)( + cudensitymatTensorCallback_t, cudensitymatElementaryOperatorSparsity_t, + int32_t, const int64_t[], const int32_t[], double, int32_t, + const double[], cudaDataType_t, void *, cudaStream_t); + + auto wrapper = [](cudensitymatTensorCallback_t callback, + cudensitymatElementaryOperatorSparsity_t sparsity, + int32_t num_modes, const int64_t modeExtents[], + const int32_t diagonal_offsets[], double time, + int32_t num_params, const double params[], + cudaDataType_t data_type, void *tensor_storage, + cudaStream_t stream) -> int32_t { + try { + auto *context = reinterpret_cast(callback); + matrix_operator &storedOp = context->tensorOp; + + if (num_modes <= 0) { + std::cerr << "num_modes is invalid: " << num_modes << std::endl; + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + if (num_params != 2 * context->paramNames.size()) + throw std::runtime_error( + fmt::format("[Internal Error] Invalid number of tensor callback " + "parameters. Expected {} double values " + "representing {} complex parameters but received " + "{}.", + std::to_string(2 * context->paramNames.size()), + std::to_string(context->paramNames.size()), + std::to_string(num_params))); + + std::unordered_map> param_map; + for (size_t i = 0; i < context->paramNames.size(); ++i) { + param_map[context->paramNames[i]] = + std::complex(params[2 * i], params[2 * i + 1]); + cudaq::debug("Tensor callback param name {}, value {}", + context->paramNames[i], param_map[context->paramNames[i]]); + } + + std::unordered_map dimensions; + for (int i = 0; i < num_modes; ++i) { + dimensions[i] = static_cast(modeExtents[i]); + } + + if (dimensions.empty()) { + std::cerr << "Dimension map is empty!" << std::endl; + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + matrix_2 matrix_data = storedOp.to_matrix(dimensions, param_map); + + std::size_t rows = matrix_data.get_rows(); + std::size_t cols = matrix_data.get_columns(); + + if (rows != cols) { + std::cerr << "Non-square matrix encountered: " << rows << "x" << cols + << std::endl; + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + const std::vector> flatMatrix = + flattenMatrixColumnMajor(matrix_data); + + if (data_type == CUDA_C_64F) { + memcpy(tensor_storage, flatMatrix.data(), + flatMatrix.size() * sizeof(cuDoubleComplex)); + } else if (data_type == CUDA_C_32F) { + std::vector> flatMatrix_float(flatMatrix.begin(), + flatMatrix.end()); + + memcpy(tensor_storage, flatMatrix_float.data(), + flatMatrix_float.size() * sizeof(cuFloatComplex)); + } else { + std::cerr << "Invalid CUDA data type: " << data_type << std::endl; + return CUDENSITYMAT_STATUS_INVALID_VALUE; + } + + return CUDENSITYMAT_STATUS_SUCCESS; + } catch (const std::exception &e) { + std::cerr << "Error in tensor callback: " << e.what() << std::endl; + return CUDENSITYMAT_STATUS_INTERNAL_ERROR; + } + }; + + cudensitymatWrappedTensorCallback_t wrappedCallback; + wrappedCallback.callback = + reinterpret_cast(storedCallbackContext); + wrappedCallback.wrapper = + reinterpret_cast(static_cast(wrapper)); + + return wrappedCallback; +} diff --git a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h new file mode 100644 index 0000000000..a22c2fd280 --- /dev/null +++ b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h @@ -0,0 +1,96 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once + +#include "cudaq/operators.h" +#include "cudaq/utils/tensor.h" +#include +#include +#include + +namespace cudaq { +namespace dynamics { +class OpConverter { +public: + OpConverter(cudensitymatHandle_t handle) : m_handle(handle){}; + + cudensitymatOperator_t convertToCudensitymatOperator( + const std::unordered_map> ¶meters, + const operator_sum &op, + const std::vector &modeExtents); + // Construct Liouvillian + cudensitymatOperator_t constructLiouvillian( + const operator_sum &ham, + const std::vector> + &collapseOperators, + const std::vector &modeExtents, + const std::unordered_map> ¶meters, + bool isMasterEquation); + + ~OpConverter(); + +private: + std::vector> + convertToCudensitymat( + const operator_sum &op, + const std::unordered_map> ¶meters, + const std::vector &modeExtents); + cudensitymatElementaryOperator_t createElementaryOperator( + const cudaq::matrix_operator &elemOp, + const std::unordered_map> ¶meters, + const std::vector &modeExtents); + cudensitymatOperatorTerm_t createProductOperatorTerm( + const std::vector &elemOps, + const std::vector &modeExtents, + const std::vector> °rees, + const std::vector> &dualModalities); + + + std::vector> + computeLindbladTerms( + const operator_sum &collapseOp, + const std::vector &modeExtents, + const std::unordered_map> ¶meters); + + struct ScalarCallBackContext { + scalar_operator scalarOp; + std::vector paramNames; + ScalarCallBackContext(const scalar_operator &scalar_op, + const std::vector ¶mNames) + : scalarOp(scalar_op), paramNames(paramNames){}; + }; + + struct TensorCallBackContext { + matrix_operator tensorOp; + std::vector paramNames; + + TensorCallBackContext(const matrix_operator &tensor_op, + const std::vector ¶m_names) + : tensorOp(tensor_op), paramNames(param_names){}; + }; + + cudensitymatWrappedScalarCallback_t + wrapScalarCallback(const scalar_operator &scalarOp, + const std::vector ¶mNames); + cudensitymatWrappedTensorCallback_t + wrapTensorCallback(const matrix_operator &matrixOp, + const std::vector ¶mNames); + +private: + cudensitymatHandle_t m_handle; + // Things that we create that need to be cleaned up. + // Use a set so that it's safe to push pointer multiple times. + std::unordered_set m_deviceBuffers; + std::unordered_set m_elementaryOperators; + std::unordered_set m_operatorTerms; + std::deque m_scalarCallbacks; + std::deque m_tensorCallbacks; +}; +} // namespace dynamics +} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index aea415f7c2..0c0ad24bb5 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -12,7 +12,6 @@ #include "cudaq/evolution.h" #include "cudm_error_handling.h" #include "cudm_expectation.h" -#include "cudm_helpers.h" #include "cudm_time_stepper.h" #include #include @@ -21,9 +20,9 @@ evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, const state &initialState, BaseIntegrator &in_integrator, - const std::vector *> + const std::vector> &collapse_operators, - const std::vector *> &observables, + const std::vector> &observables, bool store_intermediate_results, std::optional shots_count) { cudensitymatHandle_t handle = dynamics::Context::getCurrentContext()->getHandle(); @@ -55,15 +54,13 @@ evolve_result evolve_single( system.collapseOps = collapse_operators; system.modeExtents = dims; integrator.set_system(system, schedule); - integrator.set_state(initial_state, 0.0); - - cudm_helper helper(handle); std::vector expectations; for (auto &obs : observables) expectations.emplace_back(cudm_expectation( - handle, helper.convert_to_cudensitymat_operator( - {}, *obs, dims))); + handle, cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator({}, obs, dims))); std::vector> expectationVals; std::vector intermediateStates; diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index acba052dd3..5c1efa2aec 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -9,8 +9,9 @@ #include "CuDensityMatState.h" #include "cudaq/dynamics_integrators.h" #include "cudm_error_handling.h" -#include "cudm_helpers.h" #include "cudm_time_stepper.h" +#include "CuDensityMatContext.h" + namespace cudaq { void runge_kutta::set_system(const SystemDynamics &system, @@ -48,18 +49,16 @@ void runge_kutta::integrate(double target_time) { auto &castSimState = *asCudmState(*m_state); std::unordered_map> params; if (!m_stepper) { - static std::unordered_map> helpers; - if (helpers.find(castSimState.get_handle()) == helpers.end()) - helpers[castSimState.get_handle()] = - std::make_unique(castSimState.get_handle()); - auto &helper = *(helpers.find(castSimState.get_handle())->second); for (const auto ¶m : m_schedule.parameters()) { params[param] = m_schedule.value_function()(param, 0.0); } - auto liouvillian = helper.construct_liouvillian( - *m_system.hamiltonian, m_system.collapseOps, m_system.modeExtents, - params, castSimState.is_density_matrix()); + auto liouvillian = + cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .constructLiouvillian(*m_system.hamiltonian, m_system.collapseOps, + m_system.modeExtents, params, + castSimState.is_density_matrix()); m_stepper = std::make_unique(castSimState.get_handle(), liouvillian); } diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 0fde3e9a85..02095aed66 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -34,7 +34,7 @@ TEST(EvolveTester, checkSimple) { integrator.dt = 0.001; integrator.order = 1; auto result = cudaq::evolve_single(ham, dims, schedule, initialState, - integrator, {}, {&pauliZ}, true); + integrator, {}, {pauliZ}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); std::vector theoryResults; @@ -69,7 +69,7 @@ TEST(EvolveTester, checkSimpleRK4) { integrator.dt = 0.001; integrator.order = 4; auto result = cudaq::evolve_single(ham, dims, schedule, initialState, - integrator, {}, {&pauliZ}, true); + integrator, {}, {pauliZ}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); std::vector theoryResults; @@ -104,7 +104,7 @@ TEST(EvolveTester, checkDensityMatrixSimple) { integrator.dt = 0.001; integrator.order = 1; auto result = cudaq::evolve_single(ham, dims, schedule, initialState, - integrator, {}, {&pauliZ}, true); + integrator, {}, {pauliZ}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); std::vector theoryResults; @@ -158,7 +158,7 @@ TEST(EvolveTester, checkCompositeSystem) { auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, integrator, {}, - {&cavity_occ_op, &atom_occ_op}, true); + {cavity_occ_op, atom_occ_op}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); @@ -215,8 +215,8 @@ TEST(EvolveTester, checkCompositeSystemWithCollapse) { std::sqrt(decayRate) * a; cudaq::operator_sum collapsedOp(collapsedOp_t); cudaq::evolve_result result = cudaq::evolve_single( - hamiltonian, dims, schedule, initialState, integrator, {&collapsedOp}, - {&cavity_occ_op, &atom_occ_op}, true); + hamiltonian, dims, schedule, initialState, integrator, {collapsedOp}, + {cavity_occ_op, atom_occ_op}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); @@ -265,7 +265,7 @@ TEST(EvolveTester, checkScalarTd) { integrator.dt = 0.001; integrator.order = 4; auto result = cudaq::evolve_single(ham, dims, schedule, initialState, - integrator, {&collapseOp}, {&obs}, true); + integrator, {collapseOp}, {obs}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), numSteps); std::vector theoryResults; From 3909ef2b3880533c54e1101e15d5afb00858d496 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 18 Feb 2025 20:33:53 +0000 Subject: [PATCH 304/311] just some clean up Signed-off-by: Bettina Heim --- python/cudaq/operator/definitions.py | 2 +- runtime/cudaq/dynamics/boson_operators.cpp | 65 ++++--- runtime/cudaq/dynamics/boson_operators.h | 18 +- runtime/cudaq/dynamics/matrix_operators.cpp | 2 +- runtime/cudaq/dynamics/operator_leafs.h | 99 ++++++----- runtime/cudaq/dynamics/product_operators.cpp | 82 +++++---- runtime/cudaq/dynamics/scalar_operators.cpp | 177 +++++++++++-------- runtime/cudaq/dynamics/spin_operators.cpp | 2 +- runtime/cudaq/operators.h | 29 ++- 9 files changed, 258 insertions(+), 218 deletions(-) diff --git a/python/cudaq/operator/definitions.py b/python/cudaq/operator/definitions.py index 25bdcfacb4..2ae89d9c69 100644 --- a/python/cudaq/operator/definitions.py +++ b/python/cudaq/operator/definitions.py @@ -16,7 +16,7 @@ # Operators as defined here (watch out of differences in convention): -# https://www.dynamiqs.org/python_api/utils/operators/sigmay.html +# https://www.dynamiqs.org/stable/python_api/utils/operators/create.html class operators: class matrices: diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 4eb43044c9..5e7cce19c8 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -16,7 +16,6 @@ namespace cudaq { -// FIXME: GET RID OF THIS AND INSTEAD MAKE SURE WE AGGREGATE TERMS PROPERLY #if !defined(NDEBUG) bool boson_operator::can_be_canonicalized = false; #endif @@ -24,14 +23,20 @@ bool boson_operator::can_be_canonicalized = false; // private helpers std::string boson_operator::op_code_to_string() const { - if (this->op_code == 1) - return "Ad"; - else if (this->op_code == 2) - return "A"; - else if (this->op_code == 3) - return "AdA"; - else - return "I"; + if (this->ad == 0 && this->a == 0) return "I"; + std::string str; + for (auto i = 0; i < ad; ++i) + str += "Ad"; + for (auto i = 0; i < a; ++i) + str += "A"; + return std::move(str); +} + +bool boson_operator::_inplace_mult(const boson_operator &other) { + if (this->a != 0 && other.ad != 0) return false; + this->a += other.a; + this->ad += other.ad; + return true; } // read-only properties @@ -44,11 +49,12 @@ std::vector boson_operator::degrees() const { return {this->target}; } // constructors -boson_operator::boson_operator(int target) : op_code(0), target(target) {} +boson_operator::boson_operator(int target) + : ad(0), a(0), target(target) {} -boson_operator::boson_operator(int target, int op_id) - : op_code(op_id), target(target) { - assert(0 <= op_id < 4); +boson_operator::boson_operator(int target, int op_id) + : ad(op_id & 1), a((op_id & 2) >> 1), target(target) { + assert(0 <= op_id < 4); } // evaluations @@ -63,21 +69,24 @@ matrix_2 boson_operator::to_matrix( std::to_string(this->target)); auto dim = it->second; + // fixme: make matrix computation more efficient auto mat = matrix_2(dim, dim); - if (this->op_code == 1) { // create - for (std::size_t i = 0; i + 1 < dim; i++) - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } else if (this->op_code == 2) { // annihilate - for (std::size_t i = 0; i + 1 < dim; i++) - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; - } else if (this->op_code == 3) { // number - for (std::size_t i = 0; i < dim; i++) - mat[{i, i}] = static_cast(i) + 0.0j; - } else { // id - mat[{0, 0}] = 1.0; - mat[{1, 1}] = 1.0; - } - return mat; + mat[{0, 0}] = 1.0; + mat[{1, 1}] = 1.0; + if (this->ad == 0 && this->a == 0) + return std::move(mat); + + auto create_mat = matrix_2(dim, dim); + for (std::size_t i = 0; i + 1 < dim; i++) + create_mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0j; + auto annihilate_mat = matrix_2(dim, dim); + for (std::size_t i = 0; i + 1 < dim; i++) + annihilate_mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; + for (auto i = 0; i < ad; ++i) + mat *= create_mat; + for (auto i = 0; i < a; ++i) + mat *= annihilate_mat; + return std::move(mat); } std::string boson_operator::to_string(bool include_degrees) const { @@ -90,7 +99,7 @@ std::string boson_operator::to_string(bool include_degrees) const { // comparisons bool boson_operator::operator==(const boson_operator &other) const { - return this->op_code == other.op_code && this->target == other.target; + return this->ad == other.ad && this->a == other.a && this->target == other.target; } // defined operators diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index 160226b59f..6af4cd1b8a 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -1,4 +1,4 @@ -/******************************************************************************* +/****************************************************************-*- C++ -*-**** * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * * All rights reserved. * * * @@ -26,20 +26,24 @@ class boson_operator : public operator_handler { friend class product_operator; private: - // 0 = I, 1 = Ad (create), 2 = A (annihilate), 3 = AdA (number) - int op_code; + + // ad * a always, otherwise define new term + // if we use the anticommutation relation, we just trade product term length for sum term length + // e.g. a ad a a ad = 2 a + 4 ad a a + ad ad a a a + uint16_t ad; + uint16_t a; int target; + // 0 = I, ad = 1, a = 2, ada = 3 boson_operator(int target, int op_code); std::string op_code_to_string() const; + bool _inplace_mult(const boson_operator &other); + public: -// FIXME: GET RID OF THIS #if !defined(NDEBUG) - static bool - can_be_canonicalized; // needs to be false; no canonical order can be - // defined for matrix operator expressions + static bool can_be_canonicalized; // cannot be canonicalized without splitting a product term into a sum of terms #endif // read-only properties diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index c238dc2e33..c39dab3e18 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -81,7 +81,7 @@ std::string matrix_operator::unique_id() const { auto str = this->op_code + std::to_string(*it); while (++it != this->targets.cend()) str += "." + std::to_string(*it); - return str; + return std::move(str); } std::vector matrix_operator::degrees() const { return this->targets; } diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h index d35bc74768..27743bdea5 100644 --- a/runtime/cudaq/dynamics/operator_leafs.h +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -80,63 +80,68 @@ class scalar_operator { // unary operators - scalar_operator operator-() const; - scalar_operator operator+() const; + scalar_operator operator-() const &; + scalar_operator operator-() &&; + scalar_operator operator+() const &; + scalar_operator operator+() &&; // right-hand arithmetics - scalar_operator operator*(double other) const; - scalar_operator operator/(double other) const; - scalar_operator operator+(double other) const; - scalar_operator operator-(double other) const; - scalar_operator &operator*=(double other); - scalar_operator &operator/=(double other); - scalar_operator &operator+=(double other); - scalar_operator &operator-=(double other); - scalar_operator operator*(std::complex other) const; - scalar_operator operator/(std::complex other) const; - scalar_operator operator+(std::complex other) const; - scalar_operator operator-(std::complex other) const; - scalar_operator &operator*=(std::complex other); - scalar_operator &operator/=(std::complex other); - scalar_operator &operator+=(std::complex other); - scalar_operator &operator-=(std::complex other); - scalar_operator operator*(const scalar_operator &other) const; - scalar_operator operator/(const scalar_operator &other) const; - scalar_operator operator+(const scalar_operator &other) const; - scalar_operator operator-(const scalar_operator &other) const; - scalar_operator &operator*=(const scalar_operator &other); - scalar_operator &operator/=(const scalar_operator &other); - scalar_operator &operator+=(const scalar_operator &other); - scalar_operator &operator-=(const scalar_operator &other); - - friend scalar_operator operator*(scalar_operator &&self, double other); - friend scalar_operator operator/(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, double other); - friend scalar_operator operator-(scalar_operator &&self, double other); - friend scalar_operator operator+(scalar_operator &&self, - std::complex other); - friend scalar_operator operator/(scalar_operator &&self, - std::complex other); - friend scalar_operator operator+(scalar_operator &&self, - std::complex other); - friend scalar_operator operator-(scalar_operator &&self, - std::complex other); + scalar_operator operator*(double other) const &; + scalar_operator operator*(double other) &&; + scalar_operator operator/(double other) const &; + scalar_operator operator/(double other) &&; + scalar_operator operator+(double other) const &; + scalar_operator operator+(double other) &&; + scalar_operator operator-(double other) const &; + scalar_operator operator-(double other) &&; + scalar_operator& operator*=(double other); + scalar_operator& operator/=(double other); + scalar_operator& operator+=(double other); + scalar_operator& operator-=(double other); + scalar_operator operator*(std::complex other) const &; + scalar_operator operator*(std::complex other) &&; + scalar_operator operator/(std::complex other) const &; + scalar_operator operator/(std::complex other) &&; + scalar_operator operator+(std::complex other) const &; + scalar_operator operator+(std::complex other) &&; + scalar_operator operator-(std::complex other) const &; + scalar_operator operator-(std::complex other) &&; + scalar_operator& operator*=(std::complex other); + scalar_operator& operator/=(std::complex other); + scalar_operator& operator+=(std::complex other); + scalar_operator& operator-=(std::complex other); + scalar_operator operator*(const scalar_operator &other) const &; + scalar_operator operator*(const scalar_operator &other) &&; + scalar_operator operator/(const scalar_operator &other) const &; + scalar_operator operator/(const scalar_operator &other) &&; + scalar_operator operator+(const scalar_operator &other) const &; + scalar_operator operator+(const scalar_operator &other) &&; + scalar_operator operator-(const scalar_operator &other) const &; + scalar_operator operator-(const scalar_operator &other) &&; + scalar_operator& operator*=(const scalar_operator &other); + scalar_operator& operator/=(const scalar_operator &other); + scalar_operator& operator+=(const scalar_operator &other); + scalar_operator& operator-=(const scalar_operator &other); // left-hand arithmetics friend scalar_operator operator*(double other, const scalar_operator &self); + friend scalar_operator operator*(double other, scalar_operator &&self); friend scalar_operator operator/(double other, const scalar_operator &self); + friend scalar_operator operator/(double other, scalar_operator &&self); friend scalar_operator operator+(double other, const scalar_operator &self); + friend scalar_operator operator+(double other, scalar_operator &&self); friend scalar_operator operator-(double other, const scalar_operator &self); - friend scalar_operator operator*(std::complex other, - const scalar_operator &self); - friend scalar_operator operator/(std::complex other, - const scalar_operator &self); - friend scalar_operator operator+(std::complex other, - const scalar_operator &self); - friend scalar_operator operator-(std::complex other, - const scalar_operator &self); + friend scalar_operator operator-(double other, scalar_operator &&self); + friend scalar_operator operator*(std::complex other, const scalar_operator &self); + friend scalar_operator operator*(std::complex other, scalar_operator &&self); + friend scalar_operator operator/(std::complex other, const scalar_operator &self); + friend scalar_operator operator/(std::complex other, scalar_operator &&self); + friend scalar_operator operator+(std::complex other, const scalar_operator &self); + friend scalar_operator operator+(std::complex other, scalar_operator &&self); + friend scalar_operator operator-(std::complex other, const scalar_operator &self); + friend scalar_operator operator-(std::complex other, scalar_operator &&self); }; template diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index b5e83ee724..ada8034484 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -58,56 +58,54 @@ product_operator::find_insert_at(const HandlerTy &other) const { .base(); // base causes insert after for reverse iterator } -template <> -std::vector::const_iterator -product_operator::find_insert_at( - const matrix_operator &other) const { - // the logic below just ensures that terms are fully or partially ordered in - // canonical order - a best effort is made to order terms, but a full - // canonical ordering is not possible for certain handlers - return std::find_if( - this->operators.crbegin(), this->operators.crend(), - [&other_degrees = static_cast &>( - other.degrees())](const matrix_operator &self_op) { - const std::vector &self_op_degrees = self_op.degrees(); - for (auto other_degree : other_degrees) { - auto item_it = std::find_if( - self_op_degrees.crbegin(), self_op_degrees.crend(), - [other_degree](int self_degree) { - return other_degree <= self_degree; - }); // FIXME: relies on canonical order - if (item_it != self_op_degrees.crend()) - return true; - } - return false; - }) - .base(); // base causes insert after for reverse iterator +template<> +std::vector::const_iterator product_operator::find_insert_at(const matrix_operator &other) const { + // the logic below just ensures that terms are fully or partially ordered in canonical order - + // a best effort is made to order terms, but a full canonical ordering is not possible for certain handlers + return std::find_if(this->operators.crbegin(), this->operators.crend(), + [&other_degrees = static_cast&>(other.degrees())] + (const matrix_operator& self_op) { + const std::vector &self_op_degrees = self_op.degrees(); + for (auto other_degree : other_degrees) { + auto item_it = std::find_if(self_op_degrees.crbegin(), self_op_degrees.crend(), + [other_degree](int self_degree) { return other_degree <= self_degree; }); // FIXME: depends on canonical order (otherwise matrix computation is rather inefficient) + if (item_it != self_op_degrees.crend()) return true; + } + return false; + }).base(); // base causes insert after for reverse iterator } -template -template ::value && - !product_operator::supports_inplace_mult, - int>> -void product_operator::insert(T &&other) { +template +template::value && + !product_operator::supports_inplace_mult, std::false_type>> +void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); this->operators.insert(pos, other); } -template -template ::value && - product_operator::supports_inplace_mult, - bool>> +template +template::value && + product_operator::supports_inplace_mult, std::true_type>> void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); - if (pos != this->operators.begin() && (pos - 1)->target == other.target) - this->coefficient *= - this->operators.erase(pos - 1, pos - 1) - ->inplace_mult( - other); // erase: constant time conversion to non-const iterator - else - this->operators.insert(pos, std::move(other)); + if (pos != this->operators.begin() && (pos - 1)->target == other.target) { + auto it = this->operators.erase(pos - 1, pos - 1); // erase: constant time conversion to non-const iterator + auto succeeded = it->inplace_mult(other); + if (!succeeded) this->operators.insert(pos, std::move(other)); + } + else this->operators.insert(pos, std::move(other)); +} + +template<> +template::value && + product_operator::supports_inplace_mult, std::true_type>> +void product_operator::insert(T &&other) { + auto pos = this->find_insert_at(other); + if (pos != this->operators.begin() && (pos - 1)->target == other.target) { + auto it = this->operators.erase(pos - 1, pos - 1); // erase: constant time conversion to non-const iterator + this->coefficient *= it->inplace_mult(other); + } + else this->operators.insert(pos, std::move(other)); } template diff --git a/runtime/cudaq/dynamics/scalar_operators.cpp b/runtime/cudaq/dynamics/scalar_operators.cpp index cbeafa19c4..055b00143e 100644 --- a/runtime/cudaq/dynamics/scalar_operators.cpp +++ b/runtime/cudaq/dynamics/scalar_operators.cpp @@ -97,23 +97,37 @@ bool scalar_operator::operator==(scalar_operator other) const { // unary operators -scalar_operator scalar_operator::operator-() const { return *this * (-1.); } +scalar_operator scalar_operator::operator-() const & { + return *this * (-1.); +} + +scalar_operator scalar_operator::operator-() && { + *this *= -1.; + return std::move(*this); +} + +scalar_operator scalar_operator::operator+() const & { + return *this; +} -scalar_operator scalar_operator::operator+() const { return *this; } +scalar_operator scalar_operator::operator+() && { + return std::move(*this); +} // right-hand arithmetics -#define ARITHMETIC_OPERATIONS(op, otherTy) \ - scalar_operator scalar_operator::operator op(otherTy other) const { \ - if (std::holds_alternative>(this->value)) { \ - return scalar_operator(std::get>(this->value) \ - op other); \ - } \ - auto newGenerator = \ - [other, generator = std::get(this->value)]( \ - const std::unordered_map> \ - ¶meters) { return generator(parameters) op other; }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS(op, otherTy) \ + scalar_operator scalar_operator::operator op(otherTy other) const & { \ + if (std::holds_alternative>(this->value)) { \ + return scalar_operator( \ + std::get>(this->value) op other); \ + } \ + auto newGenerator = \ + [other, generator = std::get(this->value)]( \ + const std::unordered_map> ¶meters) { \ + return generator(parameters) op other; \ + }; \ + return scalar_operator(std::move(newGenerator)); \ } ARITHMETIC_OPERATIONS(*, double); @@ -125,21 +139,21 @@ ARITHMETIC_OPERATIONS(/, std::complex); ARITHMETIC_OPERATIONS(+, std::complex); ARITHMETIC_OPERATIONS(-, std::complex); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ - scalar_operator scalar_operator::operator op(const scalar_operator &other) \ - const { \ - if (std::holds_alternative>(this->value) && \ - std::holds_alternative>(other.value)) { \ - return scalar_operator(std::get>( \ - this->value) op std::get>(other.value)); \ - } \ - auto newGenerator = \ - [other, \ - *this](const std::unordered_map> \ - ¶meters) { \ - return this->evaluate(parameters) op other.evaluate(parameters); \ - }; \ - return scalar_operator(newGenerator); \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS(op) \ + scalar_operator scalar_operator::operator op( \ + const scalar_operator &other) const & { \ + if (std::holds_alternative>(this->value) && \ + std::holds_alternative>(other.value)) { \ + return scalar_operator( \ + std::get>(this->value) op \ + std::get>(other.value)); \ + } \ + auto newGenerator = \ + [other, *this]( \ + const std::unordered_map> ¶meters) { \ + return this->evaluate(parameters) op other.evaluate(parameters); \ + }; \ + return scalar_operator(std::move(newGenerator)); \ } ARITHMETIC_OPERATIONS_SCALAR_OPS(*); @@ -147,19 +161,19 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS(/); ARITHMETIC_OPERATIONS_SCALAR_OPS(+); ARITHMETIC_OPERATIONS_SCALAR_OPS(-); -#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ - scalar_operator &scalar_operator::operator op##=(otherTy other) { \ - if (std::holds_alternative>(this->value)) { \ - this->value = std::get>(this->value) op other; \ - return *this; \ - } \ - auto newGenerator = \ - [other, generator = \ - std::move(std::get(this->value))]( \ - const std::unordered_map> \ - ¶meters) { return generator(parameters) op## = other; }; \ - this->value = newGenerator; \ - return *this; \ +#define ARITHMETIC_OPERATIONS_ASSIGNMENT(op, otherTy) \ + scalar_operator& scalar_operator::operator op##=(otherTy other) { \ + if (std::holds_alternative>(this->value)) { \ + this->value = std::get>(this->value) op other; \ + return *this; \ + } \ + auto newGenerator = \ + [other, generator = std::move(std::get(this->value))]( \ + const std::unordered_map> ¶meters) { \ + return generator(parameters) op##= other; \ + }; \ + this->value = std::move(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_ASSIGNMENT(*, double); @@ -171,23 +185,23 @@ ARITHMETIC_OPERATIONS_ASSIGNMENT(/, std::complex); ARITHMETIC_OPERATIONS_ASSIGNMENT(+, std::complex); ARITHMETIC_OPERATIONS_ASSIGNMENT(-, std::complex); -#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ - scalar_operator &scalar_operator::operator op##=( \ - const scalar_operator &other) { \ - if (std::holds_alternative>(this->value) && \ - std::holds_alternative>(other.value)) { \ - this->value = std::get>(this->value) \ - op std::get>(other.value); \ - return *this; \ - } \ - auto newGenerator = \ - [other, \ - *this](const std::unordered_map> \ - ¶meters) { \ - return this->evaluate(parameters) op## = other.evaluate(parameters); \ - }; \ - this->value = newGenerator; \ - return *this; \ +#define ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(op) \ + scalar_operator& scalar_operator::operator op##=( \ + const scalar_operator &other) { \ + if (std::holds_alternative>(this->value) && \ + std::holds_alternative>(other.value)) { \ + this->value = \ + std::get>(this->value) op \ + std::get>(other.value); \ + return *this; \ + } \ + auto newGenerator = \ + [other, *this]( \ + const std::unordered_map> ¶meters) { \ + return this->evaluate(parameters) op##= other.evaluate(parameters); \ + }; \ + this->value = std::move(newGenerator); \ + return *this; \ } ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(*); @@ -195,9 +209,10 @@ ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(/); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(+); ARITHMETIC_OPERATIONS_SCALAR_OPS_ASSIGNMENT(-); -#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ - scalar_operator operator op(scalar_operator &&self, otherTy other) { \ - return std::move(self op## = other); \ +#define ARITHMETIC_OPERATIONS_RVALUE(op, otherTy) \ + scalar_operator scalar_operator::operator op(otherTy other) && { \ + *this op##= other; \ + return std::move(*this); \ } ARITHMETIC_OPERATIONS_RVALUE(*, double); @@ -211,18 +226,34 @@ ARITHMETIC_OPERATIONS_RVALUE(-, std::complex); // left-hand arithmetics -#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ - scalar_operator operator op(otherTy other, const scalar_operator &self) { \ - if (std::holds_alternative>(self.value)) { \ - return scalar_operator( \ - other op std::get>(self.value)); \ - } \ - auto newGenerator = \ - [other, generator = std::get(self.value)]( \ - const std::unordered_map> \ - ¶meters) { return other op generator(parameters); }; \ - return scalar_operator(newGenerator); \ - } +#define ARITHMETIC_OPERATIONS_REVERSE(op, otherTy) \ + \ + scalar_operator operator op(otherTy other, const scalar_operator &self) { \ + if (std::holds_alternative>(self.value)) { \ + return scalar_operator( \ + other op std::get>(self.value)); \ + } \ + auto newGenerator = \ + [other, generator = std::get(self.value)]( \ + const std::unordered_map> ¶meters) { \ + return other op generator(parameters); \ + }; \ + return scalar_operator(std::move(newGenerator)); \ + } \ + \ + scalar_operator operator op(otherTy other, scalar_operator &&self) { \ + if (std::holds_alternative>(self.value)) { \ + return scalar_operator( \ + other op std::get>(self.value)); \ + } \ + auto newGenerator = \ + [other, generator = std::move(std::get(self.value))]( \ + const std::unordered_map> ¶meters) { \ + return other op generator(parameters); \ + }; \ + self.value = std::move(newGenerator); \ + return std::move(self); \ + } \ ARITHMETIC_OPERATIONS_REVERSE(*, double); ARITHMETIC_OPERATIONS_REVERSE(/, double); diff --git a/runtime/cudaq/dynamics/spin_operators.cpp b/runtime/cudaq/dynamics/spin_operators.cpp index 99cec8eccd..b7add7efc2 100644 --- a/runtime/cudaq/dynamics/spin_operators.cpp +++ b/runtime/cudaq/dynamics/spin_operators.cpp @@ -86,7 +86,7 @@ matrix_2 spin_operator::to_matrix( mat[{0, 0}] = 1.0; mat[{1, 1}] = 1.0; } - return mat; + return std::move(mat); } std::string spin_operator::to_string(bool include_degrees) const { diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 09694ea79c..8c08e7c8a9 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -312,16 +312,11 @@ class product_operator { private: // template defined as long as T implements an in-place multiplication - // won't work if the in-place multiplication was inherited from a base class - template ::value, - bool> = true> - static std::true_type handler_mult(int); - template - static std::false_type handler_mult( - ...); // ellipsis ensures the template above is picked if it exists - static constexpr bool supports_inplace_mult = - std::is_same(0)), std::true_type>::value; + template + static decltype(std::declval().inplace_mult(std::declval())) handler_mult(int); + template + static std::false_type handler_mult(...); // ellipsis ensures the template above is picked if it exists + static constexpr bool supports_inplace_mult = !std::is_same(0)), std::false_type>::value; #if !defined(NDEBUG) bool is_canonicalized() const; @@ -330,16 +325,14 @@ class product_operator { typename std::vector::const_iterator find_insert_at(const HandlerTy &other) const; - template ::value && - !product_operator::supports_inplace_mult, - int> = 0> + template::value && + !product_operator::supports_inplace_mult, + std::false_type> = std::false_type()> void insert(T &&other); - template ::value && - product_operator::supports_inplace_mult, - bool> = true> + template::value && + product_operator::supports_inplace_mult, + std::true_type> = std::true_type()> void insert(T &&other); std::string get_term_id() const; From 4307a89628eca33a57dbfb20a6322ba81b0ac0a1 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Tue, 18 Feb 2025 21:40:22 +0000 Subject: [PATCH 305/311] just some clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/boson_operators.cpp | 2 +- runtime/cudaq/dynamics/boson_operators.h | 4 +- runtime/cudaq/dynamics/matrix_operators.cpp | 84 +--- runtime/cudaq/dynamics/matrix_operators.h | 4 +- unittests/dynamics/matrix_operator.cpp | 243 +++++----- unittests/dynamics/operator_conversions.cpp | 104 ++-- unittests/dynamics/operator_sum.cpp | 513 ++++++++++++++------ unittests/dynamics/product_operator.cpp | 445 ++++++++++++----- 8 files changed, 862 insertions(+), 537 deletions(-) diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 5e7cce19c8..c6e83dd4ec 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -32,7 +32,7 @@ std::string boson_operator::op_code_to_string() const { return std::move(str); } -bool boson_operator::_inplace_mult(const boson_operator &other) { +bool boson_operator::inplace_mult(const boson_operator &other) { if (this->a != 0 && other.ad != 0) return false; this->a += other.a; this->ad += other.ad; diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index 6af4cd1b8a..1e011f64c5 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -27,7 +27,7 @@ class boson_operator : public operator_handler { private: - // ad * a always, otherwise define new term + // ad * a always, otherwise define new product operator // if we use the anticommutation relation, we just trade product term length for sum term length // e.g. a ad a a ad = 2 a + 4 ad a a + ad ad a a a uint16_t ad; @@ -39,7 +39,7 @@ class boson_operator : public operator_handler { std::string op_code_to_string() const; - bool _inplace_mult(const boson_operator &other); + bool inplace_mult(const boson_operator &other); public: #if !defined(NDEBUG) diff --git a/runtime/cudaq/dynamics/matrix_operators.cpp b/runtime/cudaq/dynamics/matrix_operators.cpp index c39dab3e18..42a9bb2efe 100644 --- a/runtime/cudaq/dynamics/matrix_operators.cpp +++ b/runtime/cudaq/dynamics/matrix_operators.cpp @@ -252,38 +252,36 @@ product_operator matrix_operator::identity(int degree) { return product_operator(matrix_operator(degree)); } -product_operator matrix_operator::annihilate(int degree) { - std::string op_code = "annihilate"; +product_operator matrix_operator::number(int degree) { + std::string op_code = "number"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i + 1 < dimension; i++) { - mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; - }; + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = static_cast(i) + 0.0j; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); return product_operator(std::move(op)); } -product_operator matrix_operator::create(int degree) { - std::string op_code = "create"; +product_operator matrix_operator::parity(int degree) { + std::string op_code = "parity"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i + 1 < dimension; i++) { - mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; - } - return mat; - }; + auto func = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + std::size_t dimension = dimensions[0]; + auto mat = matrix_2(dimension, dimension); + for (std::size_t i = 0; i < dimension; i++) { + mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; + } + return mat; + }; matrix_operator::define(op_code, {-1}, func); } auto op = matrix_operator(op_code, {degree}); @@ -337,44 +335,6 @@ product_operator matrix_operator::momentum(int degree) { return product_operator(std::move(op)); } -product_operator matrix_operator::number(int degree) { - std::string op_code = "number"; - if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = static_cast(i) + 0.0j; - } - return mat; - }; - matrix_operator::define(op_code, {-1}, func); - } - auto op = matrix_operator(op_code, {degree}); - return product_operator(std::move(op)); -} - -product_operator matrix_operator::parity(int degree) { - std::string op_code = "parity"; - if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { - auto func = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - std::size_t dimension = dimensions[0]; - auto mat = matrix_2(dimension, dimension); - for (std::size_t i = 0; i < dimension; i++) { - mat[{i, i}] = std::pow(-1., static_cast(i)) + 0.0j; - } - return mat; - }; - matrix_operator::define(op_code, {-1}, func); - } - auto op = matrix_operator(op_code, {degree}); - return product_operator(std::move(op)); -} - product_operator matrix_operator::displace(int degree) { std::string op_code = "displace"; if (matrix_operator::m_ops.find(op_code) == matrix_operator::m_ops.end()) { diff --git a/runtime/cudaq/dynamics/matrix_operators.h b/runtime/cudaq/dynamics/matrix_operators.h index 5c355e282f..86bf4e88de 100644 --- a/runtime/cudaq/dynamics/matrix_operators.h +++ b/runtime/cudaq/dynamics/matrix_operators.h @@ -153,12 +153,10 @@ class matrix_operator : public operator_handler { static product_operator identity(); static product_operator identity(int degree); - static product_operator annihilate(int degree); - static product_operator create(int degree); - static product_operator momentum(int degree); static product_operator number(int degree); static product_operator parity(int degree); static product_operator position(int degree); + static product_operator momentum(int degree); /// Operators that accept parameters at runtime. static product_operator squeeze(int degree); static product_operator displace(int degree); diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 44ef8efb6d..513c855fb8 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -11,9 +11,8 @@ #include TEST(OperatorExpressions, checkMatrixOpsUnary) { - auto create = cudaq::matrix_operator::create(0); - utils::checkEqual((-create).to_matrix({{0, 2}}), - -1.0 * utils::create_matrix(2)); + auto create = cudaq::matrix_operator::position(0); + utils::checkEqual((-create).to_matrix({{0,2}}), -1.0 * utils::position_matrix(2)); } TEST(OperatorExpressions, checkPreBuiltMatrixOps) { @@ -35,9 +34,9 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { // Annihilation operator. { for (auto level_count : levels) { - auto annihilate = cudaq::matrix_operator::annihilate(degree_index); + auto annihilate = cudaq::matrix_operator::momentum(degree_index); auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); - auto want_annihilate = utils::annihilate_matrix(level_count); + auto want_annihilate = utils::momentum_matrix(level_count); utils::checkEqual(want_annihilate, got_annihilate); } } @@ -45,9 +44,9 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { // Creation operator. { for (auto level_count : levels) { - auto create = cudaq::matrix_operator::create(degree_index); + auto create = cudaq::matrix_operator::position(degree_index); auto got_create = create.to_matrix({{degree_index, level_count}}); - auto want_create = utils::create_matrix(level_count); + auto want_create = utils::position_matrix(level_count); utils::checkEqual(want_create, got_create); } } @@ -122,24 +121,20 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { std::unordered_map dimensions = { {0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; - { - auto func0 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1])); - ; - }; - auto func1 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1])); - ; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::position_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } // op 0: // momentum level+1 on 0 @@ -150,27 +145,31 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 3}); - auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), - utils::position_matrix(level_count + 2)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 2), - utils::number_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), + utils::position_matrix(level_count + 2)); + auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 2), + utils::number_matrix(level_count)); std::vector product_matrices = { utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * - utils::create_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1)}; - std::vector product_reverse_matrices = { + utils::position_matrix(level_count + 2) * utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1) + }; + std::vector product_reverse_matrices = { + utils::number_matrix(level_count), + utils::position_matrix(level_count + 2) * utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1) + }; + std::vector sum_matrices_term0 = { + utils::id_matrix(level_count), + utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1) + }; + std::vector sum_matrices_term1 = { utils::number_matrix(level_count), - utils::create_matrix(level_count + 2) * - utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1)}; - std::vector sum_matrices_term0 = { - utils::id_matrix(level_count), utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1)}; - std::vector sum_matrices_term1 = { - utils::number_matrix(level_count), utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1)}; + utils::position_matrix(level_count + 2), + utils::id_matrix(level_count + 1) + }; auto expected_product = cudaq::kronecker(product_matrices.begin(), product_matrices.end()); @@ -206,7 +205,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { // `matrix_operator` + `complex` and `complex` + // `matrix_operator` { - auto elementary = cudaq::matrix_operator::annihilate(0); + auto elementary = cudaq::matrix_operator::momentum(0); auto sum = value + elementary; auto reverse = elementary + value; @@ -215,8 +214,8 @@ TEST(OperatorExpressions, checkMatrixOpsWithComplex) { auto got_matrix_reverse = reverse.to_matrix({{0, 3}}); auto scaled_identity = value * utils::id_matrix(3); - auto want_matrix = scaled_identity + utils::annihilate_matrix(3); - auto want_matrix_reverse = utils::annihilate_matrix(3) + scaled_identity; + auto want_matrix = scaled_identity + utils::momentum_matrix(3); + auto want_matrix_reverse = utils::momentum_matrix(3) + scaled_identity; utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_matrix_reverse, got_matrix_reverse); @@ -278,7 +277,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { // `matrix_operator + scalar_operator` { - auto self = cudaq::matrix_operator::annihilate(0); + auto self = cudaq::matrix_operator::momentum(0); auto other = cudaq::scalar_operator(const_scale_factor); auto sum = self + other; @@ -290,9 +289,10 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); - auto want_matrix = utils::annihilate_matrix(level_count) + scaled_identity; + auto want_matrix = + utils::momentum_matrix(level_count) + scaled_identity; auto want_reverse_matrix = - scaled_identity + utils::annihilate_matrix(level_count); + scaled_identity + utils::momentum_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -392,7 +392,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { // `matrix_operator * scalar_operator` { - auto self = cudaq::matrix_operator::create(0); + auto self = cudaq::matrix_operator::position(0); auto other = cudaq::scalar_operator(function); auto product = self * other; @@ -403,13 +403,11 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { ASSERT_TRUE(reverse.degrees() == want_degrees); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}, - {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix( - {{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto want_matrix = utils::create_matrix(level_count) * scaled_identity; + auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::position_matrix(level_count) * scaled_identity; auto want_reverse_matrix = - scaled_identity * utils::create_matrix(level_count); + scaled_identity * utils::position_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -424,29 +422,30 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { // Addition, same DOF. { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(0); + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::matrix_operator::position(0); auto sum = self + other; ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = utils::annihilate_matrix(level_count) + - utils::create_matrix(level_count); + auto want_matrix = utils::momentum_matrix(level_count) + + utils::position_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); } // Addition, different DOF's. { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(1); + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::matrix_operator::position(1); auto sum = self + other; ASSERT_TRUE(sum.num_terms() == 2); - auto annihilate_full = cudaq::kronecker( - utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); - auto create_full = cudaq::kronecker(utils::create_matrix(level_count), + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::momentum_matrix(level_count)); + auto create_full = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); auto want_matrix = annihilate_full + create_full; @@ -455,29 +454,30 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { // Subtraction, same DOF. { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(0); + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::matrix_operator::position(0); auto sum = self - other; ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = utils::annihilate_matrix(level_count) - - utils::create_matrix(level_count); + auto want_matrix = utils::momentum_matrix(level_count) - + utils::position_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); } // Subtraction, different DOF's. { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(1); + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::matrix_operator::position(1); auto sum = self - other; ASSERT_TRUE(sum.num_terms() == 2); - auto annihilate_full = cudaq::kronecker( - utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); - auto create_full = cudaq::kronecker(utils::create_matrix(level_count), + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::momentum_matrix(level_count)); + auto create_full = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = annihilate_full - create_full; @@ -486,8 +486,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { // Multiplication, same DOF. { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(0); + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::matrix_operator::position(0); auto product = self * other; ASSERT_TRUE(product.num_terms() == 2); @@ -496,15 +496,15 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { ASSERT_TRUE(product.degrees() == want_degrees); auto got_matrix = product.to_matrix(dimensions); - auto want_matrix = utils::annihilate_matrix(level_count) * - utils::create_matrix(level_count); + auto want_matrix = utils::momentum_matrix(level_count) * + utils::position_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); } // Multiplication, different DOF's. { - auto self = cudaq::matrix_operator::annihilate(0); - auto other = cudaq::matrix_operator::create(1); + auto self = cudaq::matrix_operator::momentum(0); + auto other = cudaq::matrix_operator::position(1); auto product = self * other; ASSERT_TRUE(product.num_terms() == 2); @@ -512,9 +512,10 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto annihilate_full = cudaq::kronecker( - utils::id_matrix(level_count), utils::annihilate_matrix(level_count)); - auto create_full = cudaq::kronecker(utils::create_matrix(level_count), + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(level_count), + utils::momentum_matrix(level_count)); + auto create_full = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = product.to_matrix(dimensions); auto want_matrix = annihilate_full * create_full; @@ -530,9 +531,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `matrix_operator + operator_sum` { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); + auto self = cudaq::matrix_operator::momentum(0); + auto operator_sum = cudaq::matrix_operator::position(0) + + cudaq::matrix_operator::identity(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -541,9 +542,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { ASSERT_TRUE(reverse.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + utils::momentum_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::create_matrix(level_count)); + utils::position_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); @@ -558,9 +559,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `matrix_operator - operator_sum` { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); + auto self = cudaq::matrix_operator::momentum(0); + auto operator_sum = cudaq::matrix_operator::position(0) + + cudaq::matrix_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -569,9 +570,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { ASSERT_TRUE(reverse.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + utils::momentum_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::create_matrix(level_count)); + utils::position_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); @@ -586,7 +587,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `matrix_operator * operator_sum` { - auto self = cudaq::matrix_operator::annihilate(0); + auto self = cudaq::matrix_operator::momentum(0); auto operator_sum = cudaq::matrix_operator::squeeze(0) + cudaq::matrix_operator::identity(1); @@ -601,7 +602,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { ASSERT_TRUE(term.num_terms() == 2); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + utils::momentum_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), utils::squeeze_matrix(level_count, value)); @@ -621,8 +622,8 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `operator_sum += matrix_operator` { - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); + auto operator_sum = cudaq::matrix_operator::position(0) + + cudaq::matrix_operator::identity(1); operator_sum += cudaq::matrix_operator::displace(0); ASSERT_TRUE(operator_sum.num_terms() == 3); @@ -631,7 +632,7 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { cudaq::kronecker(utils::id_matrix(level_count), utils::displace_matrix(level_count, value)); auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::create_matrix(level_count)); + utils::position_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); @@ -643,16 +644,16 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `operator_sum -= matrix_operator` { - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); - operator_sum -= cudaq::matrix_operator::annihilate(0); + auto operator_sum = cudaq::matrix_operator::position(0) + + cudaq::matrix_operator::identity(1); + operator_sum -= cudaq::matrix_operator::momentum(0); ASSERT_TRUE(operator_sum.num_terms() == 3); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + utils::momentum_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::create_matrix(level_count)); + utils::position_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); @@ -664,9 +665,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { // `operator_sum *= matrix_operator` { - auto self = cudaq::matrix_operator::annihilate(0); - auto operator_sum = - cudaq::matrix_operator::create(0) + cudaq::matrix_operator::identity(1); + auto self = cudaq::matrix_operator::momentum(0); + auto operator_sum = cudaq::matrix_operator::position(0) + + cudaq::matrix_operator::identity(1); operator_sum *= self; @@ -675,9 +676,9 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { ASSERT_TRUE(term.num_terms() == 2); auto self_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); + utils::momentum_matrix(level_count)); auto term_0_full = cudaq::kronecker(utils::id_matrix(level_count), - utils::create_matrix(level_count)); + utils::position_matrix(level_count)); auto term_1_full = cudaq::kronecker(utils::id_matrix(level_count), utils::id_matrix(level_count)); auto sum_full = term_0_full + term_1_full; @@ -690,25 +691,21 @@ TEST(OperatorExpressions, checkMatrixOpsAdvancedArithmetics) { } TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { - auto op1 = cudaq::matrix_operator::create(2); - auto op2 = cudaq::matrix_operator::annihilate(0); + auto op1 = cudaq::matrix_operator::position(2); + auto op2 = cudaq::matrix_operator::momentum(0); std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; { - auto func0 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1])); - ; - }; - auto func1 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1])); - ; - }; + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::position_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index ee3ff7ceaa..2205cc8fbb 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -32,17 +32,13 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { utils::checkEqual(got, expected); }; - auto checkProductEquals = - [dimensions, - parameters](cudaq::product_operator prod, - cudaq::matrix_2 expected, bool aggregated_prod = false) { - auto expected_num_terms = 2; - if (aggregated_prod) - expected_num_terms = prod.degrees().size(); - auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; + auto checkProductEquals = [dimensions, parameters]( + cudaq::product_operator prod, + cudaq::matrix_2 expected, int expected_num_terms = 2) { + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; // `elementary + elementary` { @@ -90,25 +86,15 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { // `elementary * elementary` { - checkProductEquals(matrix_elementary * matrix_elementary, - matrix_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * spin_elementary, - spin_elementary_expected * spin_elementary_expected, - true); - checkProductEquals(boson_elementary * boson_elementary, - boson_elementary_expected * boson_elementary_expected); - checkProductEquals(matrix_elementary * spin_elementary, - matrix_elementary_expected * spin_elementary_expected); - checkProductEquals(spin_elementary * matrix_elementary, - spin_elementary_expected * matrix_elementary_expected); - checkProductEquals(matrix_elementary * boson_elementary, - matrix_elementary_expected * boson_elementary_expected); - checkProductEquals(boson_elementary * matrix_elementary, - boson_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * boson_elementary, - spin_elementary_expected * boson_elementary_expected); - checkProductEquals(boson_elementary * spin_elementary, - boson_elementary_expected * spin_elementary_expected); + checkProductEquals(matrix_elementary * matrix_elementary, matrix_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * spin_elementary, spin_elementary_expected * spin_elementary_expected, 1); + checkProductEquals(boson_elementary * boson_elementary, boson_elementary_expected * boson_elementary_expected, 1); + checkProductEquals(matrix_elementary * spin_elementary, matrix_elementary_expected * spin_elementary_expected); + checkProductEquals(spin_elementary * matrix_elementary, spin_elementary_expected * matrix_elementary_expected); + checkProductEquals(matrix_elementary * boson_elementary, matrix_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * matrix_elementary, boson_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * boson_elementary, spin_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * spin_elementary, boson_elementary_expected * spin_elementary_expected); } // `elementary *= elementary` @@ -120,14 +106,11 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { auto spin_product = cudaq::product_operator(spin_elementary); spin_product *= spin_elementary; - checkProductEquals(spin_product, - spin_elementary_expected * spin_elementary_expected, - true); + checkProductEquals(spin_product, spin_elementary_expected * spin_elementary_expected, 1); auto boson_product = cudaq::product_operator(boson_elementary); boson_product *= boson_elementary; - checkProductEquals(boson_product, - boson_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_product, boson_elementary_expected * boson_elementary_expected, 1); matrix_product = cudaq::product_operator(matrix_elementary); matrix_product *= spin_elementary; @@ -167,17 +150,13 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { utils::checkEqual(got, expected); }; - auto checkProductEquals = - [dimensions, - parameters](cudaq::product_operator prod, - cudaq::matrix_2 expected, bool aggregated_prod = false) { - auto expected_num_terms = 4; - if (aggregated_prod) - expected_num_terms = prod.degrees().size(); - auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; + auto checkProductEquals = [dimensions, parameters]( + cudaq::product_operator prod, + cudaq::matrix_2 expected, int expected_num_terms = 4) { + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; // `product + product` { @@ -225,24 +204,15 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { // `product * product` { - checkProductEquals(matrix_product * matrix_product, - matrix_product_expected * matrix_product_expected); - checkProductEquals(spin_product * spin_product, - spin_product_expected * spin_product_expected, true); - checkProductEquals(boson_product * boson_product, - boson_product_expected * boson_product_expected); - checkProductEquals(matrix_product * spin_product, - matrix_product_expected * spin_product_expected); - checkProductEquals(spin_product * matrix_product, - spin_product_expected * matrix_product_expected); - checkProductEquals(matrix_product * boson_product, - matrix_product_expected * boson_product_expected); - checkProductEquals(boson_product * matrix_product, - boson_product_expected * matrix_product_expected); - checkProductEquals(spin_product * boson_product, - spin_product_expected * boson_product_expected); - checkProductEquals(boson_product * spin_product, - boson_product_expected * spin_product_expected); + checkProductEquals(matrix_product * matrix_product, matrix_product_expected * matrix_product_expected); + checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected, 2); + checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected, 3); + checkProductEquals(matrix_product * spin_product, matrix_product_expected * spin_product_expected); + checkProductEquals(spin_product * matrix_product, spin_product_expected * matrix_product_expected); + checkProductEquals(matrix_product * boson_product, matrix_product_expected * boson_product_expected); + checkProductEquals(boson_product * matrix_product, boson_product_expected * matrix_product_expected); + checkProductEquals(spin_product * boson_product, spin_product_expected * boson_product_expected); + checkProductEquals(boson_product * spin_product, boson_product_expected * spin_product_expected); } // `product *= product` @@ -254,13 +224,11 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { auto spin_product_0 = spin_product; spin_product_0 *= spin_product; - checkProductEquals(spin_product_0, - spin_product_expected * spin_product_expected, true); + checkProductEquals(spin_product_0, spin_product_expected * spin_product_expected, 2); auto boson_product_0 = boson_product; boson_product_0 *= boson_product; - checkProductEquals(boson_product_0, - boson_product_expected * boson_product_expected); + checkProductEquals(boson_product_0, boson_product_expected * boson_product_expected, 3); matrix_product_0 = matrix_product; matrix_product_0 *= spin_product; diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index 341edd32df..ccfd6cd11b 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -196,10 +196,148 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { auto op_matrix = utils::annihilate_matrix(2); auto scalar_matrix = value_0 * utils::id_matrix(2); - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::number(5); + auto op1 = cudaq::matrix_operator::parity(5); + + auto sum = op0 + op1; + ASSERT_TRUE(sum.degrees() == want_degrees); + + auto got_matrix = sum.to_matrix({{5, level_count}}); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); + auto want_matrix = matrix0 + matrix1; + utils::checkEqual(want_matrix, got_matrix); + } + } + + // Different degrees of freedom. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(1); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {1, 0}; + auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::number(0); + auto op1 = cudaq::matrix_operator::parity(1); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 + fullHilbert1; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + } + + // Different degrees of freedom, non-consecutive. + // Should produce the same matrices as the above test. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::number(0); + auto op1 = cudaq::matrix_operator::parity(2); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 + fullHilbert1; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix, got_matrix_reverse); + } + } + + // Different degrees of freedom, non-consecutive but all dimensions + // provided. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_sum = spin0 + spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + + cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); + std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; + + ASSERT_TRUE(spin_sum.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::number(0); + auto op1 = cudaq::matrix_operator::parity(2); + + auto got = op0 + op1; + auto got_reverse = op1 + op0; + + std::vector want_degrees = {2, 0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + dimensions = {{0, level_count},{1, level_count},{2, level_count}}; + auto got_matrix = got.to_matrix(dimensions); + auto got_matrix_reverse = got_reverse.to_matrix(dimensions); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); + std::vector matrices_0 = {identity, matrix0}; + std::vector matrices_1 = {matrix1, identity}; + + auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 + fullHilbert1; + auto want_matrix_reverse = fullHilbert1 + fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(got_matrix, want_matrix); + } + } } // spin operator against constant @@ -209,14 +347,73 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { auto sum = scalar_op + op; auto reverse = op + scalar_op; - std::vector want_degrees = {0}; - auto op_matrix = utils::PauliX_matrix(); - auto scalar_matrix = value_0 * utils::id_matrix(2); + // matrix operator against constant + { + auto op = cudaq::matrix_operator::parity(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::parity_matrix(2); + auto scalar_matrix = value_0 * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); + } - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); + // spin operator against constant + { + auto op = cudaq::spin_operator::x(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = value_0 * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); + } + + // matrix operator against constant from lambda + { + auto op = cudaq::matrix_operator::parity(1); + auto scalar_op = cudaq::scalar_operator(function); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::parity_matrix(2); + auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); + } + + // spin operator against constant from lambda + { + auto op = cudaq::spin_operator::x(1); + auto scalar_op = cudaq::scalar_operator(function); + auto sum = scalar_op + op; + auto reverse = op + scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); + + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); + } } // matrix operator against constant from lambda @@ -295,8 +492,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` { - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto sum = original + value; auto reverse = value + original; @@ -309,8 +506,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); @@ -347,8 +544,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + scalar_operator` { level_count = 2; - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -361,8 +558,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = @@ -432,8 +629,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` { - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto difference = original - value; auto reverse = value - original; @@ -447,8 +644,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = @@ -462,8 +659,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - scalar_operator` { - auto original = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -477,8 +674,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = @@ -492,8 +689,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -519,8 +716,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); @@ -532,8 +729,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto product = sum * value; auto reverse = value * sum; @@ -557,8 +754,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto scaled_identity = value * utils::id_matrix((level_count) * (level_count + 1)); @@ -570,8 +767,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * scalar_operator` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -595,8 +792,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { reverse.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; auto scaled_identity = @@ -723,8 +920,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= scalar_operator` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::momentum(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::momentum(2); sum *= cudaq::scalar_operator(value); @@ -738,7 +935,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils::id_matrix(level_count + 1), utils::create_matrix(level_count)}; + utils::id_matrix(level_count + 1), + utils::parity_matrix(level_count)}; std::vector matrices_2 = { utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); @@ -752,8 +950,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); sum += double_value; @@ -762,8 +960,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto scaled_identity = double_value * utils::id_matrix((level_count) * (level_count + 1)); @@ -841,8 +1039,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); sum -= double_value; @@ -851,8 +1049,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { auto got_matrix = sum.to_matrix({{1, level_count}, {2, level_count + 1}}); auto matrix0 = cudaq::kronecker(utils::id_matrix(level_count + 1), - utils::create_matrix(level_count)); - auto matrix1 = cudaq::kronecker(utils::create_matrix(level_count + 1), + utils::parity_matrix(level_count)); + auto matrix1 = cudaq::kronecker(utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)); auto sum_matrix = matrix0 + matrix1; @@ -888,7 +1086,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= scalar_operator` { auto sum = cudaq::matrix_operator::number(1) + - cudaq::matrix_operator::annihilate(2); + cudaq::matrix_operator::identity(2); sum -= cudaq::scalar_operator(value); @@ -900,7 +1098,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { std::vector matrices_1 = { utils::id_matrix(level_count + 1), utils::number_matrix(level_count)}; std::vector matrices_2 = { - utils::annihilate_matrix(level_count + 1), + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); @@ -944,10 +1142,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum += product_operator` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::number(1); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); sum += product; @@ -956,19 +1154,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::number_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -983,10 +1184,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum -= product_operator` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::number(1); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); sum -= product; @@ -995,19 +1196,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::number_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1022,10 +1226,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum *= product_operator` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto product = cudaq::matrix_operator::number(0) * + cudaq::matrix_operator::number(1); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); sum *= product; @@ -1037,19 +1241,22 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count)}; + utils::id_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::number_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 2), - utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 2), + utils::id_matrix(level_count + 1), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1069,11 +1276,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { - auto sum_0 = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum_0 = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto sum_1 = cudaq::matrix_operator::parity(0) + - cudaq::matrix_operator::annihilate(1) + - cudaq::matrix_operator::create(3); + cudaq::matrix_operator::number(1) + + cudaq::matrix_operator::parity(3); auto sum = sum_0 + sum_1; @@ -1090,11 +1297,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = { - utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), - utils::create_matrix(level_count + 2), + utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_0 = { @@ -1102,9 +1310,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_2 = {utils::create_matrix(level_count + 3), + matrices_1_2 = {utils::parity_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; @@ -1123,10 +1331,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum - operator_sum` { - auto sum_0 = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::position(2); + auto sum_0 = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::position(2); auto sum_1 = cudaq::matrix_operator::parity(0) + - cudaq::matrix_operator::annihilate(1) + + cudaq::matrix_operator::number(1) + cudaq::matrix_operator::momentum(3); auto difference = sum_0 - sum_1; @@ -1144,9 +1352,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = { - utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::position_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1156,7 +1365,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_2 = {utils::momentum_matrix(level_count + 3), utils::id_matrix(level_count + 2), @@ -1177,11 +1386,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum * operator_sum` { - auto sum_0 = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum_0 = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto sum_1 = cudaq::matrix_operator::parity(0) + - cudaq::matrix_operator::annihilate(1) + - cudaq::matrix_operator::create(3); + cudaq::matrix_operator::number(1) + + cudaq::matrix_operator::parity(3); auto sum_product = sum_0 * sum_1; auto sum_product_reverse = sum_1 * sum_0; @@ -1209,11 +1418,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = { - utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), - utils::create_matrix(level_count + 2), + utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_0 = { @@ -1221,9 +1431,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_2 = {utils::create_matrix(level_count + 3), + matrices_1_2 = {utils::parity_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; @@ -1244,11 +1454,11 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum *= operator_sum` { - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto sum = cudaq::matrix_operator::parity(1) + + cudaq::matrix_operator::parity(2); auto sum_1 = cudaq::matrix_operator::parity(0) + - cudaq::matrix_operator::annihilate(1) + - cudaq::matrix_operator::create(3); + cudaq::matrix_operator::number(1) + + cudaq::matrix_operator::parity(3); sum *= sum_1; @@ -1267,11 +1477,12 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = { - utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::create_matrix(level_count + 1), utils::id_matrix(level_count)}; + matrices_0_0 = {utils::id_matrix(level_count + 3), + utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), + utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), - utils::create_matrix(level_count + 2), + utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_0 = { @@ -1279,9 +1490,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; matrices_1_1 = {utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), - utils::annihilate_matrix(level_count + 1), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; - matrices_1_2 = {utils::create_matrix(level_count + 3), + matrices_1_2 = {utils::parity_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; @@ -1306,24 +1517,20 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { {2, level_count}, {3, level_count + 3}}; - { - auto func0 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1])); - ; - }; - auto func1 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1])); - ; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::parity_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); @@ -1335,8 +1542,9 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { std::vector matrices_0 = { utils::id_matrix(level_count), utils::position_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; - std::vector matrices_1 = { - utils::number_matrix(level_count), utils::create_matrix(level_count + 2), + std::vector matrices_1 = { + utils::number_matrix(level_count), + utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1)}; auto sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); @@ -1359,19 +1567,20 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { difference = op0 - op1; difference_reverse = op1 - op0; - matrices_0 = {utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count), - utils::id_matrix(level_count + 1)}; - matrices_1 = {utils::id_matrix(level_count + 3), - utils::create_matrix(level_count), - utils::number_matrix(level_count + 1)}; - sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - diff_reverse_expected = - cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + matrices_0 = { + utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count), + utils::id_matrix(level_count + 1)}; + matrices_1 = { + utils::id_matrix(level_count + 3), + utils::parity_matrix(level_count), + utils::number_matrix(level_count + 1)}; + sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); utils::checkEqual(sum.to_matrix(dimensions), sum_expected); utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index 5a65a1e5ea..13591f2401 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -201,10 +201,153 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { std::vector want_degrees = {0}; auto op_matrix = utils::annihilate_matrix(2); - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); - utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::position(5); + auto op1 = cudaq::matrix_operator::momentum(5); + + auto got = op0 * op1; + utils::assert_product_equal(got, 1., {op0.get_terms()[0], op1.get_terms()[0]}); + ASSERT_TRUE(got.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{5, level_count}}); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); + auto want_matrix = matrix0 * matrix1; + utils::checkEqual(want_matrix, got_matrix); + } + } + + // Different degrees of freedom. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(1); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {1, 0}; + auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::position(0); + auto op1 = cudaq::matrix_operator::momentum(1); + + cudaq::product_operator got = op0 * op1; + cudaq::product_operator got_reverse = op1 * op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + } + + // Different degrees of freedom, non-consecutive. + // Should produce the same matrices as the above test. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix()); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::position(0); + auto op1 = cudaq::matrix_operator::momentum(2); + + cudaq::product_operator got = op0 * op1; + cudaq::product_operator got_reverse = op1 * op0; + + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); + auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); + + auto fullHilbert0 = cudaq::kronecker(identity, matrix0); + auto fullHilbert1 = cudaq::kronecker(matrix1, identity); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + } + + // Different degrees of freedom, non-consecutive but all dimensions + // provided. + { + auto spin0 = cudaq::spin_operator::x(0); + auto spin1 = cudaq::spin_operator::z(2); + auto spin_prod = spin0 * spin1; + + std::vector want_degrees = {2, 0}; + auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); + std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; + + ASSERT_TRUE(spin_prod.degrees() == want_degrees); + utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); + + for (auto level_count : levels) { + auto op0 = cudaq::matrix_operator::position(0); + auto op1 = cudaq::matrix_operator::momentum(2); + + cudaq::product_operator got = op0 * op1; + cudaq::product_operator got_reverse = op1 * op0; + + std::vector want_degrees = {2, 0}; + ASSERT_TRUE(got.degrees() == want_degrees); + ASSERT_TRUE(got_reverse.degrees() == want_degrees); + + dimensions = {{0, level_count},{1, level_count},{2, level_count}}; + auto got_matrix = got.to_matrix(dimensions); + auto got_matrix_reverse = got_reverse.to_matrix(dimensions); + + auto identity = utils::id_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); + + std::vector matrices_0; + std::vector matrices_1; + matrices_0 = {identity, matrix0}; + matrices_1 = {matrix1, identity}; + + auto fullHilbert0 = + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + auto fullHilbert1 = + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + auto want_matrix = fullHilbert0 * fullHilbert1; + auto want_matrix_reverse = fullHilbert1 * fullHilbert0; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(got_matrix, want_matrix); + } + } } // spin operator against constant @@ -214,13 +357,69 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { auto product = scalar_op * op; auto reverse = op * scalar_op; - std::vector want_degrees = {0}; - auto op_matrix = utils::PauliX_matrix(); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(value_0 * op_matrix, product.to_matrix()); - utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); + // matrix operator against constant + { + auto op = cudaq::matrix_operator::position(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::position_matrix(2); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); + } + + // spin operator against constant + { + auto op = cudaq::spin_operator::x(0); + auto scalar_op = cudaq::scalar_operator(value_0); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix()); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); + } + + // matrix operator against constant from lambda + { + auto op = cudaq::matrix_operator::position(1); + auto scalar_op = cudaq::scalar_operator(function); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::position_matrix(2); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({{1, 2}}, {{"value", 0.3}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); + } + + // spin operator against constant from lambda + { + auto op = cudaq::spin_operator::x(1); + auto scalar_op = cudaq::scalar_operator(function); + auto product = scalar_op * op; + auto reverse = op * scalar_op; + + std::vector want_degrees = {1}; + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({}, {{"value", 0.3}})); + utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({}, {{"value", 0.3}})); + } } // matrix operator against constant from lambda @@ -267,8 +466,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + double` { - auto product_op = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto sum = 2.0 + product_op; auto reverse = product_op + 2.0; @@ -285,8 +484,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); @@ -300,8 +499,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + complex` { - auto product_op = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto sum = value_0 + product_op; auto reverse = product_op + value_0; @@ -318,8 +517,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = @@ -363,8 +562,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator + scalar_operator` { - auto product_op = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto scalar_op = cudaq::scalar_operator(value_0); auto sum = scalar_op + product_op; @@ -382,8 +581,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = @@ -398,8 +597,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - double` { - auto product_op = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto difference = 2.0 - product_op; auto reverse = product_op - 2.0; @@ -417,8 +616,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); @@ -461,8 +660,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator - complex` { - auto product_op = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); + auto product_op = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); auto difference = value_0 - product_op; auto reverse = product_op - value_0; @@ -480,8 +679,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { reverse.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::annihilate_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto product = term_0 * term_1; auto scaled_identity = @@ -680,8 +879,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { /// `product_operator *= double` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::create(1); + auto product = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::momentum(1); product *= 2.0; ASSERT_TRUE(product.num_terms() == 2); @@ -694,8 +893,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstScalars) { auto got_matrix = product.to_matrix({{0, level_count}, {1, level_count}}); auto term_0 = cudaq::kronecker(utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)); - auto term_1 = cudaq::kronecker(utils::create_matrix(level_count), + utils::position_matrix(level_count)); + auto term_1 = cudaq::kronecker(utils::momentum_matrix(level_count), utils::id_matrix(level_count)); auto product_matrix = term_0 * term_1; auto scaled_identity = 2.0 * utils::id_matrix(level_count * level_count); @@ -794,10 +993,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator + product_operator` { - auto term_0 = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::annihilate(2); + auto term_0 = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); + auto term_1 = cudaq::matrix_operator::momentum(1) * + cudaq::matrix_operator::position(2); auto sum = term_0 + term_1; @@ -812,17 +1011,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_1; matrices_0_0 = {utils::id_matrix(level_count + 1), utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)}; + utils::position_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), + utils::position_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0; std::vector matrices_1_1; matrices_1_0 = {utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; - matrices_1_1 = {utils::annihilate_matrix(level_count + 1), + matrices_1_1 = {utils::position_matrix(level_count + 1), utils::id_matrix(level_count), utils::id_matrix(level_count)}; @@ -878,10 +1077,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator - product_operator` { - auto term_0 = cudaq::matrix_operator::annihilate(0) * + auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::number(1); - auto term_1 = - cudaq::matrix_operator::create(1) * cudaq::matrix_operator::momentum(2); + auto term_1 = cudaq::matrix_operator::momentum(1) * + cudaq::matrix_operator::momentum(2); auto difference = term_0 - term_1; @@ -893,7 +1092,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_1; matrices_0_0 = {utils::id_matrix(level_count + 1), utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)}; + utils::position_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 1), utils::number_matrix(level_count), utils::id_matrix(level_count)}; @@ -901,7 +1100,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_1_0; std::vector matrices_1_1; matrices_1_0 = {utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; matrices_1_1 = {utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count), @@ -958,9 +1157,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator * product_operator` { auto term_0 = cudaq::matrix_operator::position(0) * - cudaq::matrix_operator::annihilate(1); - auto term_1 = - cudaq::matrix_operator::create(1) * cudaq::matrix_operator::parity(2); + cudaq::matrix_operator::position(1); + auto term_1 = cudaq::matrix_operator::momentum(1) * + cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -974,13 +1173,13 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { utils::id_matrix(level_count), utils::position_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), + utils::position_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0; std::vector matrices_1_1; matrices_1_0 = {utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; matrices_1_1 = {utils::parity_matrix(level_count + 1), utils::id_matrix(level_count), @@ -1042,10 +1241,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator *= product_operator` { - auto term_0 = cudaq::matrix_operator::annihilate(0) * + auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::number(1); - auto term_1 = cudaq::matrix_operator::create(1) * - cudaq::matrix_operator::annihilate(2); + auto term_1 = cudaq::matrix_operator::momentum(1) * + cudaq::matrix_operator::position(2); term_0 *= term_1; @@ -1057,7 +1256,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_0_1; matrices_0_0 = {utils::id_matrix(level_count + 1), utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)}; + utils::position_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 1), utils::number_matrix(level_count), utils::id_matrix(level_count)}; @@ -1065,9 +1264,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { std::vector matrices_1_0; std::vector matrices_1_1; matrices_1_0 = {utils::id_matrix(level_count + 1), - utils::create_matrix(level_count), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; - matrices_1_1 = {utils::annihilate_matrix(level_count + 1), + matrices_1_1 = {utils::position_matrix(level_count + 1), utils::id_matrix(level_count), utils::id_matrix(level_count)}; @@ -1079,9 +1278,7 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - auto term1_only_matrix = - cudaq::kronecker(utils::annihilate_matrix(level_count + 1), - utils::create_matrix(level_count)); + auto term1_only_matrix = cudaq::kronecker(utils::position_matrix(level_count + 1), utils::momentum_matrix(level_count)); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(term1_only_matrix, term_1.to_matrix(dimensions)); } @@ -1134,10 +1331,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator + operator_sum` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto original_sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto product = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); + auto original_sum = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::momentum(2); auto sum = product + original_sum; auto reverse = original_sum + product; @@ -1150,16 +1347,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { std::vector matrices_0_0 = { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)}; + utils::position_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), utils::id_matrix(level_count)}; - std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), utils::create_matrix(level_count), + utils::position_matrix(level_count), utils::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils::id_matrix(level_count + 1), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 1), utils::id_matrix(level_count), - utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), + utils::id_matrix(level_count), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1212,10 +1410,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator - operator_sum` { - auto product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto original_difference = - cudaq::matrix_operator::create(1) - cudaq::matrix_operator::create(2); + auto product = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); + auto original_difference = cudaq::matrix_operator::momentum(1) - + cudaq::matrix_operator::momentum(2); auto difference = product - original_difference; auto reverse = original_difference - product; @@ -1228,16 +1426,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { std::vector matrices_0_0 = { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)}; + utils::position_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), utils::id_matrix(level_count)}; - std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), utils::create_matrix(level_count), + utils::position_matrix(level_count), utils::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils::id_matrix(level_count + 1), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 1), utils::id_matrix(level_count), - utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), + utils::id_matrix(level_count), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1291,10 +1490,10 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { // `product_operator * operator_sum` { - auto original_product = cudaq::matrix_operator::annihilate(0) * - cudaq::matrix_operator::annihilate(1); - auto sum = - cudaq::matrix_operator::create(1) + cudaq::matrix_operator::create(2); + auto original_product = cudaq::matrix_operator::position(0) * + cudaq::matrix_operator::position(1); + auto sum = cudaq::matrix_operator::momentum(1) + + cudaq::matrix_operator::momentum(2); auto product = original_product * sum; auto reverse = sum * original_product; @@ -1307,16 +1506,17 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { std::vector matrices_0_0 = { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), - utils::annihilate_matrix(level_count)}; + utils::position_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 1), - utils::annihilate_matrix(level_count), utils::id_matrix(level_count)}; - std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), utils::create_matrix(level_count), + utils::position_matrix(level_count), utils::id_matrix(level_count)}; + std::vector matrices_1_0 = { + utils::id_matrix(level_count + 1), + utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::create_matrix(level_count + 1), utils::id_matrix(level_count), - utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), + utils::id_matrix(level_count), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1376,24 +1576,20 @@ TEST(OperatorExpressions, checkCustomProductOps) { {2, level_count}, {3, level_count + 3}}; - { - auto func0 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1])); - ; - }; - auto func1 = - [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::create_matrix(dimensions[0]), - utils::number_matrix(dimensions[1])); - ; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + { + auto func0 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1]));; + }; + auto func1 = [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::number_matrix(dimensions[1]));; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); @@ -1402,15 +1598,13 @@ TEST(OperatorExpressions, checkCustomProductOps) { std::vector matrices = { utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * - utils::create_matrix(level_count + 2), + utils::position_matrix(level_count + 2) * utils::momentum_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; auto expected = cudaq::kronecker(matrices.begin(), matrices.end()); std::vector matrices_reverse = { utils::number_matrix(level_count), - utils::create_matrix(level_count + 2) * - utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 2) * utils::position_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; auto expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); @@ -1423,18 +1617,17 @@ TEST(OperatorExpressions, checkCustomProductOps) { product = op0 * op1; reverse = op1 * op0; - matrices = {utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count) * - utils::create_matrix(level_count), - utils::number_matrix(level_count + 1)}; - expected = cudaq::kronecker(matrices.begin(), matrices.end()); - - matrices_reverse = {utils::position_matrix(level_count + 3), - utils::create_matrix(level_count) * - utils::momentum_matrix(level_count), - utils::number_matrix(level_count + 1)}; - expected_reverse = - cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); + matrices = { + utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count) * utils::momentum_matrix(level_count), + utils::number_matrix(level_count + 1)}; + expected = cudaq::kronecker(matrices.begin(), matrices.end()); + + matrices_reverse = { + utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count) * utils::momentum_matrix(level_count), + utils::number_matrix(level_count + 1)}; + expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); utils::checkEqual(product.to_matrix(dimensions), expected); utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); From b0f60cee7fe900a1421309331db29af28b0314e7 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Feb 2025 15:44:04 +0000 Subject: [PATCH 306/311] full in-place multiplication for bosons Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/boson_operators.cpp | 107 +++++--- runtime/cudaq/dynamics/boson_operators.h | 10 +- unittests/CMakeLists.txt | 1 + unittests/dynamics/boson_operator.cpp | 269 ++++++++++++++++++++ unittests/dynamics/operator_conversions.cpp | 4 +- unittests/dynamics/utils.cpp | 18 +- 6 files changed, 362 insertions(+), 47 deletions(-) create mode 100644 unittests/dynamics/boson_operator.cpp diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index c6e83dd4ec..050ad6ea0d 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -6,6 +6,7 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include #include #include #include @@ -16,26 +17,57 @@ namespace cudaq { -#if !defined(NDEBUG) -bool boson_operator::can_be_canonicalized = false; -#endif - // private helpers std::string boson_operator::op_code_to_string() const { - if (this->ad == 0 && this->a == 0) return "I"; + if (this->additional_terms == 0 && this->number_offsets.size() == 0) return "I"; std::string str; - for (auto i = 0; i < ad; ++i) + for (auto offset : this->number_offsets) { + if (offset == 0) str += "N"; + else str += "(N" + std::to_string(offset) + ")"; + } + for (auto i = 0; i < this->additional_terms; ++i) str += "Ad"; - for (auto i = 0; i < a; ++i) + for (auto i = 0; i > this->additional_terms; --i) str += "A"; return std::move(str); } bool boson_operator::inplace_mult(const boson_operator &other) { - if (this->a != 0 && other.ad != 0) return false; - this->a += other.a; - this->ad += other.ad; + if (this->additional_terms > 0) { // we have "unpaired" creation operators + // first permute all number operators of RHS to the left; + // for each permutation, we acquire an addition +1 for that number operator + for (auto offset : other.number_offsets) + this->number_offsets.push_back(offset - this->additional_terms); + // now we can combine the creations in the LHS with the annihilations in the RHS; + // using ad*a = N and ad*N = (N - 1)*ad, each created number operator has an offset + // of -(x - 1 - i), where x is the number of creation operators, and i is the number + // of creation operators we already combined + auto nr_pairs = std::min(-other.additional_terms, this->additional_terms); + for (auto i = 1; i <= nr_pairs; ++i) + this->number_offsets.push_back(i - this->additional_terms); + // finally, we update the number of remaining unpaired operators + this->additional_terms += other.additional_terms; + } else if (this->additional_terms < 0) { // we have "unpaired" annihilation operators + // first permute all number operators of RHS to the left; + // for each permutation, we acquire an addition +1 for that number operator + for (auto offset : other.number_offsets) + this->number_offsets.push_back(offset - this->additional_terms); + // now we can combine the annihilations in the LHS with the creations in the RHS; + // using a*ad = (N + 1) and a*N = (N + 1)*a, each created number operator has an offset + // of (x - i), where x is the number of annihilation operators, and i is the number + // of annihilation operators we already combined + auto nr_pairs = std::min(other.additional_terms, -this->additional_terms); + for (auto i = 0; i < nr_pairs; ++i) + this->number_offsets.push_back(-this->additional_terms - i); + // finally, we update the number of remaining unpaired operators + this->additional_terms += other.additional_terms; + } else { // we only have number operators + this->number_offsets.reserve(this->number_offsets.size() + other.number_offsets.size()); + for (auto offset : other.number_offsets) + this->number_offsets.push_back(offset); + this->additional_terms = other.additional_terms; + } return true; } @@ -50,11 +82,17 @@ std::vector boson_operator::degrees() const { return {this->target}; } // constructors boson_operator::boson_operator(int target) - : ad(0), a(0), target(target) {} + : target(target), additional_terms(0) {} boson_operator::boson_operator(int target, int op_id) - : ad(op_id & 1), a((op_id & 2) >> 1), target(target) { + : target(target), additional_terms(0) { assert(0 <= op_id < 4); + if (op_id == 1) // create + this->additional_terms = 1; + else if (op_id == 2) // annihilate + this->additional_terms = -1; + else if (op_id == 3) // number + this->number_offsets.push_back(0); } // evaluations @@ -69,23 +107,32 @@ matrix_2 boson_operator::to_matrix( std::to_string(this->target)); auto dim = it->second; - // fixme: make matrix computation more efficient auto mat = matrix_2(dim, dim); - mat[{0, 0}] = 1.0; - mat[{1, 1}] = 1.0; - if (this->ad == 0 && this->a == 0) - return std::move(mat); - - auto create_mat = matrix_2(dim, dim); - for (std::size_t i = 0; i + 1 < dim; i++) - create_mat[{i + 1, i}] = std::sqrt(static_cast(i + 1)) + 0.0j; - auto annihilate_mat = matrix_2(dim, dim); - for (std::size_t i = 0; i + 1 < dim; i++) - annihilate_mat[{i, i + 1}] = std::sqrt(static_cast(i + 1)) + 0.0j; - for (auto i = 0; i < ad; ++i) - mat *= create_mat; - for (auto i = 0; i < a; ++i) - mat *= annihilate_mat; + for (std::size_t i = 0; i < dim; i++) { + mat[{i, i}] = 1.; + for (auto offset : this->number_offsets) + mat[{i, i}] *= (i + offset); + } + + if (this->additional_terms > 0) { + auto create_mat = matrix_2(dim, dim); + for (std::size_t i = 0; i + this->additional_terms < dim; i++) { + create_mat[{i + this->additional_terms, i}] = 1.; + auto &entry = create_mat[{i + this->additional_terms, i}]; + for (auto offset = this->additional_terms; offset > 0; --offset) + entry *= std::sqrt(i + offset); + } + mat *= std::move(create_mat); + } else if (this->additional_terms < 0) { + auto annihilate_mat = matrix_2(dim, dim); + for (std::size_t i = 0; i - this->additional_terms < dim; i++) { + annihilate_mat[{i, i - this->additional_terms}] = 1.; + auto &entry = annihilate_mat[{i, i - this->additional_terms}]; + for (auto offset = -this->additional_terms; offset > 0; --offset) + entry *= std::sqrt(i + offset); + } + mat *= std::move(annihilate_mat); + } return std::move(mat); } @@ -99,7 +146,9 @@ std::string boson_operator::to_string(bool include_degrees) const { // comparisons bool boson_operator::operator==(const boson_operator &other) const { - return this->ad == other.ad && this->a == other.a && this->target == other.target; + return this->additional_terms == other.additional_terms && + this->number_offsets == other.number_offsets && + this->target == other.target; } // defined operators diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index 1e011f64c5..20c72f7cb7 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -27,12 +27,9 @@ class boson_operator : public operator_handler { private: - // ad * a always, otherwise define new product operator - // if we use the anticommutation relation, we just trade product term length for sum term length - // e.g. a ad a a ad = 2 a + 4 ad a a + ad ad a a a - uint16_t ad; - uint16_t a; int target; + int additional_terms; + std::vector number_offsets; // 0 = I, ad = 1, a = 2, ada = 3 boson_operator(int target, int op_code); @@ -42,9 +39,6 @@ class boson_operator : public operator_handler { bool inplace_mult(const boson_operator &other); public: -#if !defined(NDEBUG) - static bool can_be_canonicalized; // cannot be canonicalized without splitting a product term into a sum of terms -#endif // read-only properties diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index f350873afc..8c153e1c38 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -268,6 +268,7 @@ set(CUDAQ_OPERATOR_TEST_SOURCES dynamics/scalar_operator.cpp dynamics/matrix_operator.cpp dynamics/spin_operator.cpp + dynamics/boson_operator.cpp dynamics/operator_conversions.cpp dynamics/product_operator.cpp dynamics/operator_sum.cpp diff --git a/unittests/dynamics/boson_operator.cpp b/unittests/dynamics/boson_operator.cpp new file mode 100644 index 0000000000..07bbd70f19 --- /dev/null +++ b/unittests/dynamics/boson_operator.cpp @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#include +#include "utils.h" +#include "cudaq/operators.h" +#include +#include "cudaq/dynamics/boson_operators.h" + +TEST(OperatorExpressions, checkBosonOpsUnary) { + auto op = cudaq::boson_operator::number(0); + utils::checkEqual((-op).to_matrix({{0, 3}}), -1.0 * utils::number_matrix(3)); +} + +TEST(OperatorExpressions, checkPreBuiltBosonOps) { + + // number operator + { + auto nr_op = cudaq::boson_operator::number(0); + for (auto d = 2; d < 7; ++d) { + auto nr_mat = utils::number_matrix(d); + for (auto pow = 1; pow < 4; ++pow) { + auto expected = nr_mat; + auto got = nr_op; + for (auto i = 1; i < pow; ++i) { + expected *= nr_mat; + got *= nr_op; + } + utils::checkEqual(expected, got.to_matrix({{0, d}})); + } + } + } + + // creation operator + { + auto ad_op = cudaq::boson_operator::create(0); + for (auto d = 2; d < 7; ++d) { + auto ad_mat = utils::create_matrix(d); + for (auto pow = 1; pow < 4; ++pow) { + auto expected = ad_mat; + auto got = ad_op; + for (auto i = 1; i < pow; ++i) { + expected *= ad_mat; + got *= ad_op; + } + utils::checkEqual(expected, got.to_matrix({{0, d}})); + } + } + } + + // annihilation operator + { + auto a_op = cudaq::boson_operator::annihilate(0); + for (auto d = 2; d < 7; ++d) { + auto a_mat = utils::annihilate_matrix(d); + for (auto pow = 1; pow < 4; ++pow) { + auto expected = a_mat; + auto got = a_op; + for (auto i = 1; i < pow; ++i) { + expected *= a_mat; + got *= a_op; + } + utils::checkEqual(expected, got.to_matrix({{0, d}})); + } + } + } + + // general in-place multiplication + { + auto max_nr_consecutive = 6; + auto nr_op = cudaq::boson_operator::number(0); + auto ad_op = cudaq::boson_operator::create(0); + auto a_op = cudaq::boson_operator::annihilate(0); + for (auto d = 3; d < 4; ++d) { + + // we use a larger dimension to compute the correct expected matrices + // to ensure the expected matrix is no impacted by finite-size errors + auto nr_mat = utils::number_matrix(d + max_nr_consecutive); + auto ad_mat = utils::create_matrix(d + max_nr_consecutive); + auto a_mat = utils::annihilate_matrix(d + max_nr_consecutive); + + for (auto nrs = 0; nrs < max_nr_consecutive; ++nrs) { + for (auto ads = 0; ads < max_nr_consecutive; ++ ads) { + for (auto as = 0; as < max_nr_consecutive; ++ as) { + + // Check Ads * Ns * As + + std::cout << "# Ads: " << ads << ", "; + std::cout << "# Ns: " << nrs << ", "; + std::cout << "# As: " << as << std::endl; + + auto padded = utils::id_matrix(d + max_nr_consecutive); + for (auto i = 0; i < ads; ++i) + padded *= ad_mat; + for (auto i = 0; i < nrs; ++i) + padded *= nr_mat; + for (auto i = 0; i < as; ++i) + padded *= a_mat; + auto expected = cudaq::matrix_2(d, d); + for (std::size_t i = 0; i < d; i++) { + for (std::size_t j = 0; j < d; j++) + expected[{i, j}] = padded[{i, j}]; + } + + auto got = cudaq::boson_operator::identity(0); + for (auto i = 0; i < ads; ++i) + got *= ad_op; + for (auto i = 0; i < nrs; ++i) + got *= nr_op; + for (auto i = 0; i < as; ++i) + got *= a_op; + + utils::checkEqual(expected, got.to_matrix({{0, d}})); + + // Check Ads * As * Ns + + std::cout << "# Ads: " << ads << ", "; + std::cout << "# As: " << as << ", "; + std::cout << "# Ns: " << nrs << std::endl; + + padded = utils::id_matrix(d + max_nr_consecutive); + for (auto i = 0; i < ads; ++i) + padded *= ad_mat; + for (auto i = 0; i < as; ++i) + padded *= a_mat; + for (auto i = 0; i < nrs; ++i) + padded *= nr_mat; + expected = cudaq::matrix_2(d, d); + for (std::size_t i = 0; i < d; i++) { + for (std::size_t j = 0; j < d; j++) + expected[{i, j}] = padded[{i, j}]; + } + + got = cudaq::boson_operator::identity(0); + for (auto i = 0; i < ads; ++i) + got *= ad_op; + for (auto i = 0; i < as; ++i) + got *= a_op; + for (auto i = 0; i < nrs; ++i) + got *= nr_op; + + utils::checkEqual(expected, got.to_matrix({{0, d}})); + + // Check Ns * Ads * As + + std::cout << "# Ns: " << nrs << ", "; + std::cout << "# Ads: " << ads << ", "; + std::cout << "# As: " << as << std::endl; + + padded = utils::id_matrix(d + max_nr_consecutive); + for (auto i = 0; i < nrs; ++i) + padded *= nr_mat; + for (auto i = 0; i < ads; ++i) + padded *= ad_mat; + for (auto i = 0; i < as; ++i) + padded *= a_mat; + expected = cudaq::matrix_2(d, d); + for (std::size_t i = 0; i < d; i++) { + for (std::size_t j = 0; j < d; j++) + expected[{i, j}] = padded[{i, j}]; + } + + got = cudaq::boson_operator::identity(0); + for (auto i = 0; i < nrs; ++i) + got *= nr_op; + for (auto i = 0; i < ads; ++i) + got *= ad_op; + for (auto i = 0; i < as; ++i) + got *= a_op; + + utils::checkEqual(expected, got.to_matrix({{0, d}})); + + // check Ns * As * Ads + + std::cout << "# Ns: " << nrs << ", "; + std::cout << "# As: " << as << ", "; + std::cout << "# Ads: " << ads << std::endl; + + padded = utils::id_matrix(d + max_nr_consecutive); + for (auto i = 0; i < nrs; ++i) + padded *= nr_mat; + for (auto i = 0; i < as; ++i) + padded *= a_mat; + for (auto i = 0; i < ads; ++i) + padded *= ad_mat; + expected = cudaq::matrix_2(d, d); + for (std::size_t i = 0; i < d; i++) { + for (std::size_t j = 0; j < d; j++) + expected[{i, j}] = padded[{i, j}]; + } + + got = cudaq::boson_operator::identity(0); + for (auto i = 0; i < nrs; ++i) + got *= nr_op; + for (auto i = 0; i < as; ++i) + got *= a_op; + for (auto i = 0; i < ads; ++i) + got *= ad_op; + + utils::checkEqual(expected, got.to_matrix({{0, d}})); + + // check As * Ns * Ads + + std::cout << "# As: " << as << ", "; + std::cout << "# Ns: " << nrs << ", "; + std::cout << "# Ads: " << ads << std::endl; + + padded = utils::id_matrix(d + max_nr_consecutive); + for (auto i = 0; i < as; ++i) + padded *= a_mat; + for (auto i = 0; i < nrs; ++i) + padded *= nr_mat; + for (auto i = 0; i < ads; ++i) + padded *= ad_mat; + expected = cudaq::matrix_2(d, d); + for (std::size_t i = 0; i < d; i++) { + for (std::size_t j = 0; j < d; j++) + expected[{i, j}] = padded[{i, j}]; + } + + got = cudaq::boson_operator::identity(0); + for (auto i = 0; i < as; ++i) + got *= a_op; + for (auto i = 0; i < nrs; ++i) + got *= nr_op; + for (auto i = 0; i < ads; ++i) + got *= ad_op; + + utils::checkEqual(expected, got.to_matrix({{0, d}})); + + // check As * Ads * Ns + + std::cout << "# As: " << as << ", "; + std::cout << "# Ads: " << ads << ", "; + std::cout << "# Ns: " << nrs << std::endl; + + padded = utils::id_matrix(d + max_nr_consecutive); + for (auto i = 0; i < as; ++i) + padded *= a_mat; + for (auto i = 0; i < ads; ++i) + padded *= ad_mat; + for (auto i = 0; i < nrs; ++i) + padded *= nr_mat; + expected = cudaq::matrix_2(d, d); + for (std::size_t i = 0; i < d; i++) { + for (std::size_t j = 0; j < d; j++) + expected[{i, j}] = padded[{i, j}]; + } + + got = cudaq::boson_operator::identity(0); + for (auto i = 0; i < as; ++i) + got *= a_op; + for (auto i = 0; i < ads; ++i) + got *= ad_op; + for (auto i = 0; i < nrs; ++i) + got *= nr_op; + + utils::checkEqual(expected, got.to_matrix({{0, d}})); + } + } + } + } + } +} \ No newline at end of file diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index 2205cc8fbb..da9b9409df 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -206,7 +206,7 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { { checkProductEquals(matrix_product * matrix_product, matrix_product_expected * matrix_product_expected); checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected, 2); - checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected, 3); + checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected, 2); checkProductEquals(matrix_product * spin_product, matrix_product_expected * spin_product_expected); checkProductEquals(spin_product * matrix_product, spin_product_expected * matrix_product_expected); checkProductEquals(matrix_product * boson_product, matrix_product_expected * boson_product_expected); @@ -228,7 +228,7 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { auto boson_product_0 = boson_product; boson_product_0 *= boson_product; - checkProductEquals(boson_product_0, boson_product_expected * boson_product_expected, 3); + checkProductEquals(boson_product_0, boson_product_expected * boson_product_expected, 2); matrix_product_0 = matrix_product; matrix_product_0 *= spin_product; diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp index 9fdb6b9900..eeac7e247e 100644 --- a/unittests/dynamics/utils.cpp +++ b/unittests/dynamics/utils.cpp @@ -6,13 +6,14 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include "cudaq/operators.h" +#include #include "cudaq/utils/tensor.h" #include namespace utils { -void print(cudaq::matrix_2 mat) { +void print(cudaq::matrix_2 mat, std::string name = "") { + if (name != "") std::cout << name << ":" << std::endl; for (std::size_t i = 0; i < mat.get_rows(); i++) { for (std::size_t j = 0; j < mat.get_columns(); j++) std::cout << mat[{i, j}] << " "; @@ -31,18 +32,19 @@ void assert_product_equal( } void checkEqual(cudaq::matrix_2 a, cudaq::matrix_2 b) { + print(a, "matrix a"); + print(b, "matrix b"); + ASSERT_EQ(a.get_rank(), b.get_rank()); ASSERT_EQ(a.get_rows(), b.get_rows()); ASSERT_EQ(a.get_columns(), b.get_columns()); ASSERT_EQ(a.get_size(), b.get_size()); for (std::size_t i = 0; i < a.get_rows(); i++) { for (std::size_t j = 0; j < a.get_columns(); j++) { - double a_real = a[{i, j}].real(); - double b_real = b[{i, j}].real(); - EXPECT_NEAR(a_real, b_real, 1e-8); - double a_imag = a[{i, j}].imag(); - double b_imag = b[{i, j}].imag(); - EXPECT_NEAR(a_imag, b_imag, 1e-8); + auto a_val = a[{i, j}]; + auto b_val = b[{i, j}]; + EXPECT_NEAR(a_val.real(), b_val.real(), 1e-8); + EXPECT_NEAR(a_val.imag(), b_val.imag(), 1e-8); } } } From c58272157803d7b822de5d8d375ba4c2e297a119 Mon Sep 17 00:00:00 2001 From: Bettina Heim Date: Wed, 19 Feb 2025 22:58:07 +0000 Subject: [PATCH 307/311] additional boson tests and clean up Signed-off-by: Bettina Heim --- runtime/cudaq/dynamics/boson_operators.cpp | 87 ++-- runtime/cudaq/dynamics/boson_operators.h | 16 +- runtime/cudaq/dynamics/product_operators.cpp | 3 +- unittests/dynamics/boson_operator.cpp | 482 ++++++++++++++++++- unittests/dynamics/matrix_operator.cpp | 40 +- unittests/dynamics/spin_operator.cpp | 8 +- unittests/dynamics/utils.cpp | 2 +- 7 files changed, 551 insertions(+), 87 deletions(-) diff --git a/runtime/cudaq/dynamics/boson_operators.cpp b/runtime/cudaq/dynamics/boson_operators.cpp index 050ad6ea0d..0df91c04b3 100644 --- a/runtime/cudaq/dynamics/boson_operators.cpp +++ b/runtime/cudaq/dynamics/boson_operators.cpp @@ -24,6 +24,7 @@ std::string boson_operator::op_code_to_string() const { std::string str; for (auto offset : this->number_offsets) { if (offset == 0) str += "N"; + else if (offset > 0) str += "(N+" + std::to_string(offset) + ")"; else str += "(N" + std::to_string(offset) + ")"; } for (auto i = 0; i < this->additional_terms; ++i) @@ -33,42 +34,32 @@ std::string boson_operator::op_code_to_string() const { return std::move(str); } -bool boson_operator::inplace_mult(const boson_operator &other) { +void boson_operator::inplace_mult(const boson_operator &other) { + this->number_offsets.reserve(this->number_offsets.size() + other.number_offsets.size()); + + // first permute all number operators of RHS to the left; for x = # permutations, + // if we have "unpaired" creation operators, the number operator becomes (N + x), + // if we have "unpaired" annihilation operators, the number operator becomes (N - x). + for (auto offset : other.number_offsets) + this->number_offsets.push_back(offset - this->additional_terms); + + // now we can combine the creation and annihilation operators; if (this->additional_terms > 0) { // we have "unpaired" creation operators - // first permute all number operators of RHS to the left; - // for each permutation, we acquire an addition +1 for that number operator - for (auto offset : other.number_offsets) - this->number_offsets.push_back(offset - this->additional_terms); - // now we can combine the creations in the LHS with the annihilations in the RHS; // using ad*a = N and ad*N = (N - 1)*ad, each created number operator has an offset // of -(x - 1 - i), where x is the number of creation operators, and i is the number // of creation operators we already combined - auto nr_pairs = std::min(-other.additional_terms, this->additional_terms); - for (auto i = 1; i <= nr_pairs; ++i) + for (auto i = 1; i <= this->additional_terms && i <= -other.additional_terms; ++i) this->number_offsets.push_back(i - this->additional_terms); - // finally, we update the number of remaining unpaired operators - this->additional_terms += other.additional_terms; } else if (this->additional_terms < 0) { // we have "unpaired" annihilation operators - // first permute all number operators of RHS to the left; - // for each permutation, we acquire an addition +1 for that number operator - for (auto offset : other.number_offsets) - this->number_offsets.push_back(offset - this->additional_terms); - // now we can combine the annihilations in the LHS with the creations in the RHS; // using a*ad = (N + 1) and a*N = (N + 1)*a, each created number operator has an offset // of (x - i), where x is the number of annihilation operators, and i is the number // of annihilation operators we already combined - auto nr_pairs = std::min(other.additional_terms, -this->additional_terms); - for (auto i = 0; i < nr_pairs; ++i) - this->number_offsets.push_back(-this->additional_terms - i); - // finally, we update the number of remaining unpaired operators - this->additional_terms += other.additional_terms; - } else { // we only have number operators - this->number_offsets.reserve(this->number_offsets.size() + other.number_offsets.size()); - for (auto offset : other.number_offsets) - this->number_offsets.push_back(offset); - this->additional_terms = other.additional_terms; + for (auto i = 0; i > this->additional_terms && i > -other.additional_terms; --i) + this->number_offsets.push_back(i - this->additional_terms); } - return true; + + // finally, we update the number of remaining unpaired operators + this->additional_terms += other.additional_terms; } // read-only properties @@ -108,30 +99,30 @@ matrix_2 boson_operator::to_matrix( auto dim = it->second; auto mat = matrix_2(dim, dim); - for (std::size_t i = 0; i < dim; i++) { - mat[{i, i}] = 1.; - for (auto offset : this->number_offsets) - mat[{i, i}] *= (i + offset); - } - if (this->additional_terms > 0) { - auto create_mat = matrix_2(dim, dim); - for (std::size_t i = 0; i + this->additional_terms < dim; i++) { - create_mat[{i + this->additional_terms, i}] = 1.; - auto &entry = create_mat[{i + this->additional_terms, i}]; + for (std::size_t column = 0; column + this->additional_terms < dim; column++) { + auto row = column + this->additional_terms; + mat[{row, column}] = 1.; + for (auto offset : this->number_offsets) + mat[{row, column}] *= (row + offset); for (auto offset = this->additional_terms; offset > 0; --offset) - entry *= std::sqrt(i + offset); + mat[{row, column}] *= std::sqrt(column + offset); } - mat *= std::move(create_mat); } else if (this->additional_terms < 0) { - auto annihilate_mat = matrix_2(dim, dim); - for (std::size_t i = 0; i - this->additional_terms < dim; i++) { - annihilate_mat[{i, i - this->additional_terms}] = 1.; - auto &entry = annihilate_mat[{i, i - this->additional_terms}]; + for (std::size_t row = 0; row - this->additional_terms < dim; row++) { + auto column = row - this->additional_terms; + mat[{row, column}] = 1.; + for (auto offset : this->number_offsets) + mat[{row, column}] *= (row + offset); for (auto offset = -this->additional_terms; offset > 0; --offset) - entry *= std::sqrt(i + offset); + mat[{row, column}] *= std::sqrt(row + offset); } - mat *= std::move(annihilate_mat); + } else { + for (std::size_t i = 0; i < dim; i++) { + mat[{i, i}] = 1.; + for (auto offset : this->number_offsets) + mat[{i, i}] *= (i + offset); + } } return std::move(mat); } @@ -177,6 +168,12 @@ product_operator boson_operator::number(int degree) { return product_operator(boson_operator(degree, 3)); } -// FIXME: add position, momentum, others? +operator_sum boson_operator::position(int degree) { + return 0.5 * (boson_operator::create(degree) + boson_operator::annihilate(degree)); +} + +operator_sum boson_operator::momentum(int degree) { + return 0.5j * (boson_operator::create(degree) - boson_operator::annihilate(degree)); +} } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/boson_operators.h b/runtime/cudaq/dynamics/boson_operators.h index 20c72f7cb7..cd76b52e93 100644 --- a/runtime/cudaq/dynamics/boson_operators.h +++ b/runtime/cudaq/dynamics/boson_operators.h @@ -20,6 +20,9 @@ namespace cudaq { template class product_operator; +template +class operator_sum; + // FIXME: rename? class boson_operator : public operator_handler { template @@ -27,16 +30,22 @@ class boson_operator : public operator_handler { private: - int target; + // Each boson operator is represented as number operators along with an + // offset to add to each number operator, as well as an integer indicating + // how many creation or annihilation terms follow the number operators. + // See the implementation of the in-place multiplication to understand + // the meaning and purpose of this representation. In short, this + // representation allows us to perform a perfect in-place multiplication. int additional_terms; std::vector number_offsets; + int target; // 0 = I, ad = 1, a = 2, ada = 3 boson_operator(int target, int op_code); std::string op_code_to_string() const; - bool inplace_mult(const boson_operator &other); + void inplace_mult(const boson_operator &other); public: @@ -81,6 +90,9 @@ class boson_operator : public operator_handler { static product_operator create(int degree); static product_operator annihilate(int degree); static product_operator number(int degree); + + static operator_sum position(int degree); + static operator_sum momentum(int degree); }; } // namespace cudaq \ No newline at end of file diff --git a/runtime/cudaq/dynamics/product_operators.cpp b/runtime/cudaq/dynamics/product_operators.cpp index ada8034484..8820c54621 100644 --- a/runtime/cudaq/dynamics/product_operators.cpp +++ b/runtime/cudaq/dynamics/product_operators.cpp @@ -90,8 +90,7 @@ void product_operator::insert(T &&other) { auto pos = this->find_insert_at(other); if (pos != this->operators.begin() && (pos - 1)->target == other.target) { auto it = this->operators.erase(pos - 1, pos - 1); // erase: constant time conversion to non-const iterator - auto succeeded = it->inplace_mult(other); - if (!succeeded) this->operators.insert(pos, std::move(other)); + it->inplace_mult(other); } else this->operators.insert(pos, std::move(other)); } diff --git a/unittests/dynamics/boson_operator.cpp b/unittests/dynamics/boson_operator.cpp index 07bbd70f19..b0e8658a68 100644 --- a/unittests/dynamics/boson_operator.cpp +++ b/unittests/dynamics/boson_operator.cpp @@ -70,7 +70,7 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { } } - // general in-place multiplication + // basic in-place multiplication { auto max_nr_consecutive = 6; auto nr_op = cudaq::boson_operator::number(0); @@ -266,4 +266,482 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { } } } -} \ No newline at end of file +} + +TEST(OperatorExpressions, checkBosonOpsWithComplex) { + std::complex value = 0.125 + 0.125j; + auto dimension = 3; + + // `boson_operator` + `complex` + { + auto elementary = cudaq::boson_operator::create(0); + + auto sum = value + elementary; + auto reverse = elementary + value; + + auto got_matrix = sum.to_matrix({{0, dimension}}); + auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); + + auto scaled_identity = value * utils::id_matrix(dimension); + auto want_matrix = scaled_identity + utils::create_matrix(dimension); + auto want_matrix_reverse = utils::create_matrix(dimension) + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `boson_operator` - `complex` + { + auto elementary = cudaq::boson_operator::number(0); + + auto difference = value - elementary; + auto reverse = elementary - value; + + auto got_matrix = difference.to_matrix({{0, dimension}}); + auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); + + auto scaled_identity = value * utils::id_matrix(dimension); + auto want_matrix = scaled_identity - utils::number_matrix(dimension); + auto want_matrix_reverse = utils::number_matrix(dimension) - scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `boson_operator` * `complex` + { + auto elementary = cudaq::boson_operator::annihilate(0); + + auto product = value * elementary; + auto reverse = elementary * value; + + auto got_matrix = product.to_matrix({{0, dimension}}); + auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); + + auto scaled_identity = value * utils::id_matrix(dimension); + auto want_matrix = scaled_identity * utils::annihilate_matrix(dimension); + auto want_matrix_reverse = utils::annihilate_matrix(dimension) * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + } + +TEST(OperatorExpressions, checkBosonOpsWithScalars) { + + auto function = [](const std::unordered_map> ¶meters) { + auto entry = parameters.find("value"); + if (entry == parameters.end()) + throw std::runtime_error("value not defined in parameters"); + return entry->second; + }; + + /// Keeping these fixed for these more simple tests. + int degree_index = 0; + int dimension = 3; + double const_scale_factor = 2.0; + + // `boson_operator + scalar_operator` + { + auto self = cudaq::boson_operator::number(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); + auto got_matrix = sum.to_matrix({{0, dimension}}); + auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}); + auto want_matrix = utils::number_matrix(dimension) + scaled_identity; + auto want_reverse_matrix = scaled_identity + utils::number_matrix(dimension); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator + scalar_operator` + { + auto self = cudaq::boson_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + auto sum = self + other; + auto reverse = other + self; + + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); + auto got_matrix = sum.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::annihilate_matrix(dimension) + scaled_identity; + auto want_reverse_matrix = scaled_identity + utils::annihilate_matrix(dimension); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator - scalar_operator` + { + auto self = cudaq::boson_operator::identity(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); + auto got_matrix = sum.to_matrix({{0, dimension}}); + auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}); + auto want_matrix = utils::id_matrix(dimension) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils::id_matrix(dimension); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator - scalar_operator` + { + auto self = cudaq::boson_operator::create(0); + auto other = cudaq::scalar_operator(function); + + auto sum = self - other; + auto reverse = other - self; + + ASSERT_TRUE(sum.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); + + auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); + auto got_matrix = sum.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::create_matrix(dimension) - scaled_identity; + auto want_reverse_matrix = scaled_identity - utils::create_matrix(dimension); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator * scalar_operator` + { + auto self = cudaq::boson_operator::number(0); + auto other = cudaq::scalar_operator(const_scale_factor); + + auto product = self * other; + auto reverse = other * self; + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); + auto got_matrix = product.to_matrix({{0, dimension}}); + auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}); + auto want_matrix = utils::number_matrix(dimension) * scaled_identity; + auto want_reverse_matrix = scaled_identity * utils::number_matrix(dimension); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator * scalar_operator` + { + auto self = cudaq::boson_operator::annihilate(0); + auto other = cudaq::scalar_operator(function); + + auto product = self * other; + auto reverse = other * self; + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + + auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); + auto got_matrix = product.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto want_matrix = utils::annihilate_matrix(dimension) * scaled_identity; + auto want_reverse_matrix = scaled_identity * utils::annihilate_matrix(dimension); + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } +} + +TEST(OperatorExpressions, checkBosonOpsSimpleArithmetics) { + std::unordered_map dimensions = {{0, 3}, {1, 2}, {2, 4}}; + + // Addition, same DOF. + { + auto self = cudaq::boson_operator::number(0); + auto other = cudaq::boson_operator::annihilate(0); + + auto sum = self + other; + ASSERT_TRUE(sum.num_terms() == 2); + + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = utils::number_matrix(3) + + utils::annihilate_matrix(3); + utils::checkEqual(want_matrix, got_matrix); + } + + // Addition, different DOF's. + { + auto self = cudaq::boson_operator::create(0); + auto other = cudaq::boson_operator::identity(1); + + auto sum = self + other; + ASSERT_TRUE(sum.num_terms() == 2); + + auto matrix_self = cudaq::kronecker(utils::id_matrix(2), + utils::create_matrix(3)); + auto matrix_other = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(3)); + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = matrix_self + matrix_other; + utils::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, same DOF. + { + auto self = cudaq::boson_operator::identity(0); + auto other = cudaq::boson_operator::number(0); + + auto sum = self - other; + ASSERT_TRUE(sum.num_terms() == 2); + + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = utils::id_matrix(3) - + utils::number_matrix(3); + utils::checkEqual(want_matrix, got_matrix); + } + + // Subtraction, different DOF's. + { + auto self = cudaq::boson_operator::annihilate(0); + auto other = cudaq::boson_operator::create(1); + + auto sum = self - other; + ASSERT_TRUE(sum.num_terms() == 2); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(2), + utils::annihilate_matrix(3)); + auto create_full = cudaq::kronecker(utils::create_matrix(2), + utils::id_matrix(3)); + auto got_matrix = sum.to_matrix(dimensions); + auto want_matrix = annihilate_full - create_full; + utils::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, same DOF. + { + auto self = cudaq::boson_operator::create(0); + auto other = cudaq::boson_operator::annihilate(0); + + auto product = self * other; + ASSERT_TRUE(product.num_terms() == 1); + + std::vector want_degrees = {0}; + ASSERT_TRUE(product.degrees() == want_degrees); + + auto got_matrix = product.to_matrix(dimensions); + auto want_matrix = utils::number_matrix(3); + utils::checkEqual(want_matrix, got_matrix); + } + + // Multiplication, different DOF's. + { + auto self = cudaq::boson_operator::position(0); + auto other = cudaq::boson_operator::momentum(1); + + auto result = self * other; // nnote that position and momentum are each 2-term sums + ASSERT_TRUE(result.num_terms() == 4); + + std::vector want_degrees = {1, 0}; + ASSERT_TRUE(result.degrees() == want_degrees); + + auto annihilate_full = + cudaq::kronecker(utils::id_matrix(2), + utils::position_matrix(3)); + auto create_full = cudaq::kronecker(utils::momentum_matrix(2), + utils::id_matrix(3)); + auto got_matrix = result.to_matrix(dimensions); + auto want_matrix = annihilate_full * create_full; + utils::checkEqual(want_matrix, got_matrix); + } +} + +TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { + + // Keeping this fixed throughout. + std::complex value = 0.125 + 0.5j; + std::unordered_map dimensions = {{0, 3}, {1, 2}, {2, 4}, {3, 2}}; + + // `boson_operator + operator_sum` + { + auto self = cudaq::boson_operator::create(2); + auto operator_sum = cudaq::boson_operator::annihilate(2) + + cudaq::boson_operator::number(1); + + auto got = self + operator_sum; + auto reverse = operator_sum + self; + + ASSERT_TRUE(got.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); + + auto self_full = cudaq::kronecker(utils::create_matrix(4), + utils::id_matrix(2)); + auto term_0_full = cudaq::kronecker(utils::annihilate_matrix(4), + utils::id_matrix(2)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(4), + utils::number_matrix(2)); + + auto got_matrix = got.to_matrix(dimensions); + auto got_reverse_matrix = reverse.to_matrix(dimensions); + auto want_matrix = self_full + term_0_full + term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full + self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator - operator_sum` + { + auto self = cudaq::boson_operator::annihilate(0); + auto operator_sum = cudaq::boson_operator::create(0) + + cudaq::boson_operator::identity(1); + + auto got = self - operator_sum; + auto reverse = operator_sum - self; + + ASSERT_TRUE(got.num_terms() == 3); + ASSERT_TRUE(reverse.num_terms() == 3); + + auto self_full = cudaq::kronecker(utils::id_matrix(2), + utils::annihilate_matrix(3)); + auto term_0_full = cudaq::kronecker(utils::id_matrix(2), + utils::create_matrix(3)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(2), + utils::id_matrix(3)); + + auto got_matrix = got.to_matrix(dimensions); + auto got_reverse_matrix = reverse.to_matrix(dimensions); + auto want_matrix = self_full - term_0_full - term_1_full; + auto want_reverse_matrix = term_0_full + term_1_full - self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `boson_operator * operator_sum` + { + auto self = cudaq::boson_operator::number(0); + auto operator_sum = cudaq::boson_operator::create(0) + + cudaq::boson_operator::number(2); + + auto got = self * operator_sum; + auto reverse = operator_sum * self; + + ASSERT_TRUE(got.num_terms() == 2); + ASSERT_TRUE(reverse.num_terms() == 2); + for (auto &term : got.get_terms()) + ASSERT_TRUE(term.num_terms() == term.degrees().size()); + for (auto &term : reverse.get_terms()) + ASSERT_TRUE(term.num_terms() == term.degrees().size()); + + auto self_full = cudaq::kronecker(utils::id_matrix(4), + utils::number_matrix(3)); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(4), + utils::create_matrix(3)); + auto term_1_full = cudaq::kronecker(utils::number_matrix(4), + utils::id_matrix(3)); + auto sum_full = term_0_full + term_1_full; + + auto got_matrix = got.to_matrix(dimensions); + auto got_reverse_matrix = reverse.to_matrix(dimensions); + auto want_matrix = self_full * sum_full; + auto want_reverse_matrix = sum_full * self_full; + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_reverse_matrix, got_reverse_matrix); + } + + // `operator_sum += boson_operator` + { + auto operator_sum = cudaq::boson_operator::momentum(0) + + cudaq::boson_operator::annihilate(2); + operator_sum += cudaq::boson_operator::position(0); + + ASSERT_TRUE(operator_sum.num_terms() == 3); + + auto term_0_full = cudaq::kronecker(utils::annihilate_matrix(4), + utils::id_matrix(3)); + auto term_1_full = cudaq::kronecker(utils::id_matrix(4), + utils::position_matrix(3)); + auto added_full = cudaq::kronecker(utils::id_matrix(4), + utils::momentum_matrix(3)); + + auto got_matrix = operator_sum.to_matrix(dimensions); + auto want_matrix = term_0_full + term_1_full + added_full; + utils::checkEqual(want_matrix, got_matrix); + } + + // `operator_sum -= boson_operator` + { + auto operator_sum = cudaq::boson_operator::create(0) + + cudaq::boson_operator::annihilate(1); + operator_sum -= cudaq::boson_operator::momentum(0); + + ASSERT_TRUE(operator_sum.num_terms() == 3); + + auto term_0_full = cudaq::kronecker(utils::id_matrix(2), + utils::create_matrix(3)); + auto term_1_full = cudaq::kronecker(utils::annihilate_matrix(2), + utils::id_matrix(3)); + auto subtr_full = cudaq::kronecker(utils::id_matrix(2), + utils::momentum_matrix(3)); + + auto got_matrix = operator_sum.to_matrix(dimensions); + auto want_matrix = term_0_full + term_1_full - subtr_full; + utils::checkEqual(want_matrix, got_matrix); + } + + // `operator_sum *= boson_operator` + { + auto operator_sum = cudaq::boson_operator::momentum(0) + + cudaq::boson_operator::momentum(1); + auto self = cudaq::boson_operator::position(0); + + operator_sum *= self; + + ASSERT_TRUE(operator_sum.num_terms() == 8); + for (auto &term : operator_sum.get_terms()) + ASSERT_TRUE(term.num_terms() == term.degrees().size()); + + // Note that here we need to again expand the matrices for the product + // computation to ensure that the expected matrix is correct. + // (naive construction with "the right size" will lead to finite size errors). + auto padded_term0 = utils::momentum_matrix(5) * utils::position_matrix(5); + auto term0 = cudaq::matrix_2(3, 3); + for (size_t i = 0; i < 3; ++i) { + for (size_t j = 0; j < 3; ++j) + term0[{i, j}] = padded_term0[{i, j}]; + } + + auto expected_term0 = cudaq::kronecker(utils::id_matrix(2), term0); + auto expected_term1 = cudaq::kronecker(utils::momentum_matrix(2), utils::position_matrix(3)); + + auto got_matrix = operator_sum.to_matrix(dimensions); + auto want_matrix = expected_term0 + expected_term1; + utils::checkEqual(want_matrix, got_matrix); + } +} + +TEST(OperatorExpressions, checkBosonOpsDegreeVerification) { + auto op1 = cudaq::boson_operator::create(2); + auto op2 = cudaq::boson_operator::annihilate(0); + std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; + + ASSERT_THROW(op1.to_matrix({}), std::runtime_error); + ASSERT_THROW(op1.to_matrix({{1, 2}}), std::runtime_error); + ASSERT_THROW((op1 * op2).to_matrix({{2, 3}}), std::runtime_error); + ASSERT_THROW((op1 + op2).to_matrix({{0, 3}}), std::runtime_error); + ASSERT_NO_THROW((op1 * op2).to_matrix(dimensions)); + ASSERT_NO_THROW((op1 + op2).to_matrix(dimensions)); +} + \ No newline at end of file diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 513c855fb8..3ce33cb341 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -31,23 +31,23 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { } } - // Annihilation operator. + // Number operator. { for (auto level_count : levels) { - auto annihilate = cudaq::matrix_operator::momentum(degree_index); - auto got_annihilate = annihilate.to_matrix({{degree_index, level_count}}); - auto want_annihilate = utils::momentum_matrix(level_count); - utils::checkEqual(want_annihilate, got_annihilate); + auto number = cudaq::matrix_operator::number(degree_index); + auto got_number = number.to_matrix({{degree_index, level_count}}); + auto want_number = utils::number_matrix(level_count); + utils::checkEqual(want_number, got_number); } } - // Creation operator. + // Parity operator. { for (auto level_count : levels) { - auto create = cudaq::matrix_operator::position(degree_index); - auto got_create = create.to_matrix({{degree_index, level_count}}); - auto want_create = utils::position_matrix(level_count); - utils::checkEqual(want_create, got_create); + auto parity = cudaq::matrix_operator::parity(degree_index); + auto got_parity = parity.to_matrix({{degree_index, level_count}}); + auto want_parity = utils::parity_matrix(level_count); + utils::checkEqual(want_parity, got_parity); } } @@ -71,26 +71,6 @@ TEST(OperatorExpressions, checkPreBuiltMatrixOps) { } } - // Number operator. - { - for (auto level_count : levels) { - auto number = cudaq::matrix_operator::number(degree_index); - auto got_number = number.to_matrix({{degree_index, level_count}}); - auto want_number = utils::number_matrix(level_count); - utils::checkEqual(want_number, got_number); - } - } - - // Parity operator. - { - for (auto level_count : levels) { - auto parity = cudaq::matrix_operator::parity(degree_index); - auto got_parity = parity.to_matrix({{degree_index, level_count}}); - auto want_parity = utils::parity_matrix(level_count); - utils::checkEqual(want_parity, got_parity); - } - } - // Displacement operator. { for (auto level_count : levels) { diff --git a/unittests/dynamics/spin_operator.cpp b/unittests/dynamics/spin_operator.cpp index 835e6e5cce..5fe909adb5 100644 --- a/unittests/dynamics/spin_operator.cpp +++ b/unittests/dynamics/spin_operator.cpp @@ -61,8 +61,7 @@ TEST(OperatorExpressions, checkPreBuiltSpinOps) { TEST(OperatorExpressions, checkSpinOpsWithComplex) { std::complex value = 0.125 + 0.125j; - // `spin_operator` + `complex` and `complex` + - // `spin_operator` + // `spin_operator` + `complex` { auto elementary = cudaq::spin_operator::y(0); @@ -80,7 +79,7 @@ TEST(OperatorExpressions, checkSpinOpsWithComplex) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `spin_operator` - `complex` and `complex` - `spin_operator` + // `spin_operator` - `complex` { auto elementary = cudaq::spin_operator::x(0); @@ -98,8 +97,7 @@ TEST(OperatorExpressions, checkSpinOpsWithComplex) { utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } - // `spin_operator` * `complex` and `complex` * - // `spin_operator` + // `spin_operator` * `complex` { auto elementary = cudaq::spin_operator::z(0); diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp index eeac7e247e..394fd24a75 100644 --- a/unittests/dynamics/utils.cpp +++ b/unittests/dynamics/utils.cpp @@ -90,7 +90,7 @@ cudaq::matrix_2 momentum_matrix(std::size_t size) { mat[{i + 1, i}] = (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; mat[{i, i + 1}] = - -1. * (0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; + (-0.5j) * std::sqrt(static_cast(i + 1)) + 0.0 * 'j'; } return mat; } From b52b3f4d5e9baf0a3fae8d1de4069f0049269466 Mon Sep 17 00:00:00 2001 From: Sachin Pisal Date: Thu, 20 Feb 2025 14:38:36 -0800 Subject: [PATCH 308/311] Merging Bettina's changes and formatting Signed-off-by: Sachin Pisal --- runtime/cudaq/dynamics/operator_leafs.h | 2 +- runtime/cudaq/operators.h | 26 +- .../cudensitymat/CuDensityMatOpConverter.h | 3 +- runtime/nvqir/cudensitymat/cudm_evolution.cpp | 3 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 16 +- .../cudensitymat/runge_kutta_integrator.cpp | 2 +- unittests/dynamics/boson_operator.cpp | 317 +++++------ unittests/dynamics/matrix_operator.cpp | 118 ++-- unittests/dynamics/operator_conversions.cpp | 98 ++-- unittests/dynamics/operator_sum.cpp | 507 +++++------------- unittests/dynamics/product_operator.cpp | 371 +++---------- unittests/dynamics/test_cudm_helpers.cpp | 6 +- unittests/dynamics/test_cudm_time_stepper.cpp | 11 +- unittests/dynamics/test_evolve_single.cpp | 22 +- unittests/dynamics/utils.cpp | 6 +- unittests/dynamics/utils.h | 3 + 16 files changed, 577 insertions(+), 934 deletions(-) diff --git a/runtime/cudaq/dynamics/operator_leafs.h b/runtime/cudaq/dynamics/operator_leafs.h index 27743bdea5..cd0cc39561 100644 --- a/runtime/cudaq/dynamics/operator_leafs.h +++ b/runtime/cudaq/dynamics/operator_leafs.h @@ -112,7 +112,7 @@ class scalar_operator { scalar_operator& operator+=(std::complex other); scalar_operator& operator-=(std::complex other); scalar_operator operator*(const scalar_operator &other) const &; - scalar_operator operator*(const scalar_operator &other) &&; + // scalar_operator operator*(const scalar_operator &other) &&; scalar_operator operator/(const scalar_operator &other) const &; scalar_operator operator/(const scalar_operator &other) &&; scalar_operator operator+(const scalar_operator &other) const &; diff --git a/runtime/cudaq/operators.h b/runtime/cudaq/operators.h index 8c08e7c8a9..06f12fd982 100644 --- a/runtime/cudaq/operators.h +++ b/runtime/cudaq/operators.h @@ -313,10 +313,14 @@ class product_operator { // template defined as long as T implements an in-place multiplication - // won't work if the in-place multiplication was inherited from a base class template - static decltype(std::declval().inplace_mult(std::declval())) handler_mult(int); - template - static std::false_type handler_mult(...); // ellipsis ensures the template above is picked if it exists - static constexpr bool supports_inplace_mult = !std::is_same(0)), std::false_type>::value; + static decltype(std::declval().inplace_mult(std::declval())) + handler_mult(int); + template + static std::false_type handler_mult( + ...); // ellipsis ensures the template above is picked if it exists + static constexpr bool supports_inplace_mult = + !std::is_same(0)), + std::false_type>::value; #if !defined(NDEBUG) bool is_canonicalized() const; @@ -325,14 +329,16 @@ class product_operator { typename std::vector::const_iterator find_insert_at(const HandlerTy &other) const; - template::value && - !product_operator::supports_inplace_mult, - std::false_type> = std::false_type()> + template ::value && + !product_operator::supports_inplace_mult, + std::false_type> = std::false_type()> void insert(T &&other); - template::value && - product_operator::supports_inplace_mult, - std::true_type> = std::true_type()> + template ::value && + product_operator::supports_inplace_mult, + std::true_type> = std::true_type()> void insert(T &&other); std::string get_term_id() const; diff --git a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h index a22c2fd280..08d1cc4178 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h +++ b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.h @@ -11,8 +11,8 @@ #include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include -#include #include +#include namespace cudaq { namespace dynamics { @@ -51,7 +51,6 @@ class OpConverter { const std::vector> °rees, const std::vector> &dualModalities); - std::vector> computeLindbladTerms( const operator_sum &collapseOp, diff --git a/runtime/nvqir/cudensitymat/cudm_evolution.cpp b/runtime/nvqir/cudensitymat/cudm_evolution.cpp index 0c0ad24bb5..2d7f5fdf50 100644 --- a/runtime/nvqir/cudensitymat/cudm_evolution.cpp +++ b/runtime/nvqir/cudensitymat/cudm_evolution.cpp @@ -20,8 +20,7 @@ evolve_result evolve_single( const operator_sum &hamiltonian, const std::map &dimensions, const Schedule &schedule, const state &initialState, BaseIntegrator &in_integrator, - const std::vector> - &collapse_operators, + const std::vector> &collapse_operators, const std::vector> &observables, bool store_intermediate_results, std::optional shots_count) { cudensitymatHandle_t handle = diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp index 8c3ea4369d..3ae86e1565 100644 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ b/runtime/nvqir/cudensitymat/cudm_helpers.cpp @@ -448,8 +448,7 @@ computeDagger(const operator_sum &sumOp) { product_operator firstDaggerTerm = computeDagger(productTerms[0]); operator_sum daggerOpSum(firstDaggerTerm); - - + for (std::size_t i = 1; i < productTerms.size(); ++i) { daggerOpSum += computeDagger(productTerms[i]); } @@ -520,7 +519,7 @@ cudm_helper::compute_lindblad_terms( lindbladTerms.emplace_back(std::make_pair(coeff, D1_term)); } - product_operator L_daggerTimesL = -0.5 * ldag * l_op; + product_operator L_daggerTimesL = -0.5 * ldag * l_op; { std::vector elem_ops; std::vector> all_degrees; @@ -582,12 +581,11 @@ cudm_helper::compute_lindblad_terms( return lindbladTerms; } -std::pair cudm_helper:: - compute_lindblad_operator_terms( - operator_sum &collapseOp, - const std::vector &mode_extents, - const std::unordered_map> - ¶meters) { +std::pair +cudm_helper::compute_lindblad_operator_terms( + operator_sum &collapseOp, + const std::vector &mode_extents, + const std::unordered_map> ¶meters) { std::unordered_map dimensions; for (int i = 0; i < mode_extents.size(); ++i) dimensions[i] = mode_extents[i]; diff --git a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp index 5c1efa2aec..946d28b96a 100644 --- a/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp +++ b/runtime/nvqir/cudensitymat/runge_kutta_integrator.cpp @@ -6,11 +6,11 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatContext.h" #include "CuDensityMatState.h" #include "cudaq/dynamics_integrators.h" #include "cudm_error_handling.h" #include "cudm_time_stepper.h" -#include "CuDensityMatContext.h" namespace cudaq { diff --git a/unittests/dynamics/boson_operator.cpp b/unittests/dynamics/boson_operator.cpp index b0e8658a68..61432eeb43 100644 --- a/unittests/dynamics/boson_operator.cpp +++ b/unittests/dynamics/boson_operator.cpp @@ -6,11 +6,10 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include -#include "utils.h" #include "cudaq/operators.h" +#include "utils.h" #include -#include "cudaq/dynamics/boson_operators.h" +#include TEST(OperatorExpressions, checkBosonOpsUnary) { auto op = cudaq::boson_operator::number(0); @@ -28,12 +27,12 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { auto expected = nr_mat; auto got = nr_op; for (auto i = 1; i < pow; ++i) { - expected *= nr_mat; + expected *= nr_mat; got *= nr_op; } utils::checkEqual(expected, got.to_matrix({{0, d}})); } - } + } } // creation operator @@ -45,12 +44,12 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { auto expected = ad_mat; auto got = ad_op; for (auto i = 1; i < pow; ++i) { - expected *= ad_mat; + expected *= ad_mat; got *= ad_op; } utils::checkEqual(expected, got.to_matrix({{0, d}})); } - } + } } // annihilation operator @@ -62,12 +61,12 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { auto expected = a_mat; auto got = a_op; for (auto i = 1; i < pow; ++i) { - expected *= a_mat; + expected *= a_mat; got *= a_op; } utils::checkEqual(expected, got.to_matrix({{0, d}})); } - } + } } // basic in-place multiplication @@ -85,8 +84,8 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { auto a_mat = utils::annihilate_matrix(d + max_nr_consecutive); for (auto nrs = 0; nrs < max_nr_consecutive; ++nrs) { - for (auto ads = 0; ads < max_nr_consecutive; ++ ads) { - for (auto as = 0; as < max_nr_consecutive; ++ as) { + for (auto ads = 0; ads < max_nr_consecutive; ++ads) { + for (auto as = 0; as < max_nr_consecutive; ++as) { // Check Ads * Ns * As @@ -144,7 +143,7 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { for (auto i = 0; i < nrs; ++i) got *= nr_op; - utils::checkEqual(expected, got.to_matrix({{0, d}})); + utils::checkEqual(expected, got.to_matrix({{0, d}})); // Check Ns * Ads * As @@ -173,12 +172,12 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { for (auto i = 0; i < as; ++i) got *= a_op; - utils::checkEqual(expected, got.to_matrix({{0, d}})); + utils::checkEqual(expected, got.to_matrix({{0, d}})); // check Ns * As * Ads std::cout << "# Ns: " << nrs << ", "; - std::cout << "# As: " << as << ", "; + std::cout << "# As: " << as << ", "; std::cout << "# Ads: " << ads << std::endl; padded = utils::id_matrix(d + max_nr_consecutive); @@ -202,11 +201,11 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { for (auto i = 0; i < ads; ++i) got *= ad_op; - utils::checkEqual(expected, got.to_matrix({{0, d}})); + utils::checkEqual(expected, got.to_matrix({{0, d}})); // check As * Ns * Ads - std::cout << "# As: " << as << ", "; + std::cout << "# As: " << as << ", "; std::cout << "# Ns: " << nrs << ", "; std::cout << "# Ads: " << ads << std::endl; @@ -231,11 +230,11 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { for (auto i = 0; i < ads; ++i) got *= ad_op; - utils::checkEqual(expected, got.to_matrix({{0, d}})); + utils::checkEqual(expected, got.to_matrix({{0, d}})); // check As * Ads * Ns - std::cout << "# As: " << as << ", "; + std::cout << "# As: " << as << ", "; std::cout << "# Ads: " << ads << ", "; std::cout << "# Ns: " << nrs << std::endl; @@ -260,7 +259,7 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { for (auto i = 0; i < nrs; ++i) got *= nr_op; - utils::checkEqual(expected, got.to_matrix({{0, d}})); + utils::checkEqual(expected, got.to_matrix({{0, d}})); } } } @@ -269,67 +268,71 @@ TEST(OperatorExpressions, checkPreBuiltBosonOps) { } TEST(OperatorExpressions, checkBosonOpsWithComplex) { - std::complex value = 0.125 + 0.125j; - auto dimension = 3; - - // `boson_operator` + `complex` - { - auto elementary = cudaq::boson_operator::create(0); - - auto sum = value + elementary; - auto reverse = elementary + value; - - auto got_matrix = sum.to_matrix({{0, dimension}}); - auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); - - auto scaled_identity = value * utils::id_matrix(dimension); - auto want_matrix = scaled_identity + utils::create_matrix(dimension); - auto want_matrix_reverse = utils::create_matrix(dimension) + scaled_identity; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `boson_operator` - `complex` - { - auto elementary = cudaq::boson_operator::number(0); - - auto difference = value - elementary; - auto reverse = elementary - value; - - auto got_matrix = difference.to_matrix({{0, dimension}}); - auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); - - auto scaled_identity = value * utils::id_matrix(dimension); - auto want_matrix = scaled_identity - utils::number_matrix(dimension); - auto want_matrix_reverse = utils::number_matrix(dimension) - scaled_identity; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - - // `boson_operator` * `complex` - { - auto elementary = cudaq::boson_operator::annihilate(0); - - auto product = value * elementary; - auto reverse = elementary * value; - - auto got_matrix = product.to_matrix({{0, dimension}}); - auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); - - auto scaled_identity = value * utils::id_matrix(dimension); - auto want_matrix = scaled_identity * utils::annihilate_matrix(dimension); - auto want_matrix_reverse = utils::annihilate_matrix(dimension) * scaled_identity; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } + std::complex value = 0.125 + 0.125j; + auto dimension = 3; + + // `boson_operator` + `complex` + { + auto elementary = cudaq::boson_operator::create(0); + + auto sum = value + elementary; + auto reverse = elementary + value; + + auto got_matrix = sum.to_matrix({{0, dimension}}); + auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); + + auto scaled_identity = value * utils::id_matrix(dimension); + auto want_matrix = scaled_identity + utils::create_matrix(dimension); + auto want_matrix_reverse = + utils::create_matrix(dimension) + scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); } + // `boson_operator` - `complex` + { + auto elementary = cudaq::boson_operator::number(0); + + auto difference = value - elementary; + auto reverse = elementary - value; + + auto got_matrix = difference.to_matrix({{0, dimension}}); + auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); + + auto scaled_identity = value * utils::id_matrix(dimension); + auto want_matrix = scaled_identity - utils::number_matrix(dimension); + auto want_matrix_reverse = + utils::number_matrix(dimension) - scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } + + // `boson_operator` * `complex` + { + auto elementary = cudaq::boson_operator::annihilate(0); + + auto product = value * elementary; + auto reverse = elementary * value; + + auto got_matrix = product.to_matrix({{0, dimension}}); + auto got_matrix_reverse = reverse.to_matrix({{0, dimension}}); + + auto scaled_identity = value * utils::id_matrix(dimension); + auto want_matrix = scaled_identity * utils::annihilate_matrix(dimension); + auto want_matrix_reverse = + utils::annihilate_matrix(dimension) * scaled_identity; + + utils::checkEqual(want_matrix, got_matrix); + utils::checkEqual(want_matrix_reverse, got_matrix_reverse); + } +} + TEST(OperatorExpressions, checkBosonOpsWithScalars) { - auto function = [](const std::unordered_map> ¶meters) { + auto function = [](const std::unordered_map> + ¶meters) { auto entry = parameters.find("value"); if (entry == parameters.end()) throw std::runtime_error("value not defined in parameters"); @@ -356,7 +359,8 @@ TEST(OperatorExpressions, checkBosonOpsWithScalars) { auto got_matrix = sum.to_matrix({{0, dimension}}); auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}); auto want_matrix = utils::number_matrix(dimension) + scaled_identity; - auto want_reverse_matrix = scaled_identity + utils::number_matrix(dimension); + auto want_reverse_matrix = + scaled_identity + utils::number_matrix(dimension); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -373,10 +377,13 @@ TEST(OperatorExpressions, checkBosonOpsWithScalars) { ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); - auto got_matrix = sum.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_matrix = + sum.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); auto want_matrix = utils::annihilate_matrix(dimension) + scaled_identity; - auto want_reverse_matrix = scaled_identity + utils::annihilate_matrix(dimension); + auto want_reverse_matrix = + scaled_identity + utils::annihilate_matrix(dimension); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -413,10 +420,13 @@ TEST(OperatorExpressions, checkBosonOpsWithScalars) { ASSERT_TRUE(reverse.num_terms() == 2); auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); - auto got_matrix = sum.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_matrix = + sum.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); auto want_matrix = utils::create_matrix(dimension) - scaled_identity; - auto want_reverse_matrix = scaled_identity - utils::create_matrix(dimension); + auto want_reverse_matrix = + scaled_identity - utils::create_matrix(dimension); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -437,7 +447,8 @@ TEST(OperatorExpressions, checkBosonOpsWithScalars) { auto got_matrix = product.to_matrix({{0, dimension}}); auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}); auto want_matrix = utils::number_matrix(dimension) * scaled_identity; - auto want_reverse_matrix = scaled_identity * utils::number_matrix(dimension); + auto want_reverse_matrix = + scaled_identity * utils::number_matrix(dimension); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -455,10 +466,13 @@ TEST(OperatorExpressions, checkBosonOpsWithScalars) { ASSERT_TRUE(reverse.degrees() == want_degrees); auto scaled_identity = const_scale_factor * utils::id_matrix(dimension); - auto got_matrix = product.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_matrix = + product.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); + auto got_reverse_matrix = + reverse.to_matrix({{0, dimension}}, {{"value", const_scale_factor}}); auto want_matrix = utils::annihilate_matrix(dimension) * scaled_identity; - auto want_reverse_matrix = scaled_identity * utils::annihilate_matrix(dimension); + auto want_reverse_matrix = + scaled_identity * utils::annihilate_matrix(dimension); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(want_reverse_matrix, got_reverse_matrix); } @@ -476,8 +490,7 @@ TEST(OperatorExpressions, checkBosonOpsSimpleArithmetics) { ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = utils::number_matrix(3) + - utils::annihilate_matrix(3); + auto want_matrix = utils::number_matrix(3) + utils::annihilate_matrix(3); utils::checkEqual(want_matrix, got_matrix); } @@ -489,15 +502,15 @@ TEST(OperatorExpressions, checkBosonOpsSimpleArithmetics) { auto sum = self + other; ASSERT_TRUE(sum.num_terms() == 2); - auto matrix_self = cudaq::kronecker(utils::id_matrix(2), - utils::create_matrix(3)); - auto matrix_other = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(3)); + auto matrix_self = + cudaq::kronecker(utils::id_matrix(2), utils::create_matrix(3)); + auto matrix_other = + cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(3)); auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = matrix_self + matrix_other; utils::checkEqual(want_matrix, got_matrix); } - + // Subtraction, same DOF. { auto self = cudaq::boson_operator::identity(0); @@ -507,11 +520,10 @@ TEST(OperatorExpressions, checkBosonOpsSimpleArithmetics) { ASSERT_TRUE(sum.num_terms() == 2); auto got_matrix = sum.to_matrix(dimensions); - auto want_matrix = utils::id_matrix(3) - - utils::number_matrix(3); + auto want_matrix = utils::id_matrix(3) - utils::number_matrix(3); utils::checkEqual(want_matrix, got_matrix); } - + // Subtraction, different DOF's. { auto self = cudaq::boson_operator::annihilate(0); @@ -521,15 +533,14 @@ TEST(OperatorExpressions, checkBosonOpsSimpleArithmetics) { ASSERT_TRUE(sum.num_terms() == 2); auto annihilate_full = - cudaq::kronecker(utils::id_matrix(2), - utils::annihilate_matrix(3)); - auto create_full = cudaq::kronecker(utils::create_matrix(2), - utils::id_matrix(3)); + cudaq::kronecker(utils::id_matrix(2), utils::annihilate_matrix(3)); + auto create_full = + cudaq::kronecker(utils::create_matrix(2), utils::id_matrix(3)); auto got_matrix = sum.to_matrix(dimensions); auto want_matrix = annihilate_full - create_full; utils::checkEqual(want_matrix, got_matrix); } - + // Multiplication, same DOF. { auto self = cudaq::boson_operator::create(0); @@ -545,23 +556,23 @@ TEST(OperatorExpressions, checkBosonOpsSimpleArithmetics) { auto want_matrix = utils::number_matrix(3); utils::checkEqual(want_matrix, got_matrix); } - + // Multiplication, different DOF's. { auto self = cudaq::boson_operator::position(0); auto other = cudaq::boson_operator::momentum(1); - auto result = self * other; // nnote that position and momentum are each 2-term sums + auto result = + self * other; // nnote that position and momentum are each 2-term sums ASSERT_TRUE(result.num_terms() == 4); std::vector want_degrees = {1, 0}; ASSERT_TRUE(result.degrees() == want_degrees); auto annihilate_full = - cudaq::kronecker(utils::id_matrix(2), - utils::position_matrix(3)); - auto create_full = cudaq::kronecker(utils::momentum_matrix(2), - utils::id_matrix(3)); + cudaq::kronecker(utils::id_matrix(2), utils::position_matrix(3)); + auto create_full = + cudaq::kronecker(utils::momentum_matrix(2), utils::id_matrix(3)); auto got_matrix = result.to_matrix(dimensions); auto want_matrix = annihilate_full * create_full; utils::checkEqual(want_matrix, got_matrix); @@ -577,8 +588,8 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { // `boson_operator + operator_sum` { auto self = cudaq::boson_operator::create(2); - auto operator_sum = cudaq::boson_operator::annihilate(2) + - cudaq::boson_operator::number(1); + auto operator_sum = + cudaq::boson_operator::annihilate(2) + cudaq::boson_operator::number(1); auto got = self + operator_sum; auto reverse = operator_sum + self; @@ -586,12 +597,12 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { ASSERT_TRUE(got.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto self_full = cudaq::kronecker(utils::create_matrix(4), - utils::id_matrix(2)); - auto term_0_full = cudaq::kronecker(utils::annihilate_matrix(4), - utils::id_matrix(2)); - auto term_1_full = cudaq::kronecker(utils::id_matrix(4), - utils::number_matrix(2)); + auto self_full = + cudaq::kronecker(utils::create_matrix(4), utils::id_matrix(2)); + auto term_0_full = + cudaq::kronecker(utils::annihilate_matrix(4), utils::id_matrix(2)); + auto term_1_full = + cudaq::kronecker(utils::id_matrix(4), utils::number_matrix(2)); auto got_matrix = got.to_matrix(dimensions); auto got_reverse_matrix = reverse.to_matrix(dimensions); @@ -604,8 +615,8 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { // `boson_operator - operator_sum` { auto self = cudaq::boson_operator::annihilate(0); - auto operator_sum = cudaq::boson_operator::create(0) + - cudaq::boson_operator::identity(1); + auto operator_sum = + cudaq::boson_operator::create(0) + cudaq::boson_operator::identity(1); auto got = self - operator_sum; auto reverse = operator_sum - self; @@ -613,14 +624,14 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { ASSERT_TRUE(got.num_terms() == 3); ASSERT_TRUE(reverse.num_terms() == 3); - auto self_full = cudaq::kronecker(utils::id_matrix(2), - utils::annihilate_matrix(3)); - auto term_0_full = cudaq::kronecker(utils::id_matrix(2), - utils::create_matrix(3)); - auto term_1_full = cudaq::kronecker(utils::id_matrix(2), - utils::id_matrix(3)); + auto self_full = + cudaq::kronecker(utils::id_matrix(2), utils::annihilate_matrix(3)); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(2), utils::create_matrix(3)); + auto term_1_full = + cudaq::kronecker(utils::id_matrix(2), utils::id_matrix(3)); - auto got_matrix = got.to_matrix(dimensions); + auto got_matrix = got.to_matrix(dimensions); auto got_reverse_matrix = reverse.to_matrix(dimensions); auto want_matrix = self_full - term_0_full - term_1_full; auto want_reverse_matrix = term_0_full + term_1_full - self_full; @@ -631,8 +642,8 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { // `boson_operator * operator_sum` { auto self = cudaq::boson_operator::number(0); - auto operator_sum = cudaq::boson_operator::create(0) + - cudaq::boson_operator::number(2); + auto operator_sum = + cudaq::boson_operator::create(0) + cudaq::boson_operator::number(2); auto got = self * operator_sum; auto reverse = operator_sum * self; @@ -644,16 +655,15 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { for (auto &term : reverse.get_terms()) ASSERT_TRUE(term.num_terms() == term.degrees().size()); - auto self_full = cudaq::kronecker(utils::id_matrix(4), - utils::number_matrix(3)); + auto self_full = + cudaq::kronecker(utils::id_matrix(4), utils::number_matrix(3)); auto term_0_full = - cudaq::kronecker(utils::id_matrix(4), - utils::create_matrix(3)); - auto term_1_full = cudaq::kronecker(utils::number_matrix(4), - utils::id_matrix(3)); + cudaq::kronecker(utils::id_matrix(4), utils::create_matrix(3)); + auto term_1_full = + cudaq::kronecker(utils::number_matrix(4), utils::id_matrix(3)); auto sum_full = term_0_full + term_1_full; - auto got_matrix = got.to_matrix(dimensions); + auto got_matrix = got.to_matrix(dimensions); auto got_reverse_matrix = reverse.to_matrix(dimensions); auto want_matrix = self_full * sum_full; auto want_reverse_matrix = sum_full * self_full; @@ -669,12 +679,12 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { ASSERT_TRUE(operator_sum.num_terms() == 3); - auto term_0_full = cudaq::kronecker(utils::annihilate_matrix(4), - utils::id_matrix(3)); - auto term_1_full = cudaq::kronecker(utils::id_matrix(4), - utils::position_matrix(3)); - auto added_full = cudaq::kronecker(utils::id_matrix(4), - utils::momentum_matrix(3)); + auto term_0_full = + cudaq::kronecker(utils::annihilate_matrix(4), utils::id_matrix(3)); + auto term_1_full = + cudaq::kronecker(utils::id_matrix(4), utils::position_matrix(3)); + auto added_full = + cudaq::kronecker(utils::id_matrix(4), utils::momentum_matrix(3)); auto got_matrix = operator_sum.to_matrix(dimensions); auto want_matrix = term_0_full + term_1_full + added_full; @@ -683,18 +693,18 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { // `operator_sum -= boson_operator` { - auto operator_sum = cudaq::boson_operator::create(0) + - cudaq::boson_operator::annihilate(1); + auto operator_sum = + cudaq::boson_operator::create(0) + cudaq::boson_operator::annihilate(1); operator_sum -= cudaq::boson_operator::momentum(0); ASSERT_TRUE(operator_sum.num_terms() == 3); - auto term_0_full = cudaq::kronecker(utils::id_matrix(2), - utils::create_matrix(3)); - auto term_1_full = cudaq::kronecker(utils::annihilate_matrix(2), - utils::id_matrix(3)); - auto subtr_full = cudaq::kronecker(utils::id_matrix(2), - utils::momentum_matrix(3)); + auto term_0_full = + cudaq::kronecker(utils::id_matrix(2), utils::create_matrix(3)); + auto term_1_full = + cudaq::kronecker(utils::annihilate_matrix(2), utils::id_matrix(3)); + auto subtr_full = + cudaq::kronecker(utils::id_matrix(2), utils::momentum_matrix(3)); auto got_matrix = operator_sum.to_matrix(dimensions); auto want_matrix = term_0_full + term_1_full - subtr_full; @@ -703,8 +713,8 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { // `operator_sum *= boson_operator` { - auto operator_sum = cudaq::boson_operator::momentum(0) + - cudaq::boson_operator::momentum(1); + auto operator_sum = + cudaq::boson_operator::momentum(0) + cudaq::boson_operator::momentum(1); auto self = cudaq::boson_operator::position(0); operator_sum *= self; @@ -715,7 +725,8 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { // Note that here we need to again expand the matrices for the product // computation to ensure that the expected matrix is correct. - // (naive construction with "the right size" will lead to finite size errors). + // (naive construction with "the right size" will lead to finite size + // errors). auto padded_term0 = utils::momentum_matrix(5) * utils::position_matrix(5); auto term0 = cudaq::matrix_2(3, 3); for (size_t i = 0; i < 3; ++i) { @@ -724,7 +735,8 @@ TEST(OperatorExpressions, checkBosonOpsAdvancedArithmetics) { } auto expected_term0 = cudaq::kronecker(utils::id_matrix(2), term0); - auto expected_term1 = cudaq::kronecker(utils::momentum_matrix(2), utils::position_matrix(3)); + auto expected_term1 = + cudaq::kronecker(utils::momentum_matrix(2), utils::position_matrix(3)); auto got_matrix = operator_sum.to_matrix(dimensions); auto want_matrix = expected_term0 + expected_term1; @@ -744,4 +756,3 @@ TEST(OperatorExpressions, checkBosonOpsDegreeVerification) { ASSERT_NO_THROW((op1 * op2).to_matrix(dimensions)); ASSERT_NO_THROW((op1 + op2).to_matrix(dimensions)); } - \ No newline at end of file diff --git a/unittests/dynamics/matrix_operator.cpp b/unittests/dynamics/matrix_operator.cpp index 3ce33cb341..e4ebcf0568 100644 --- a/unittests/dynamics/matrix_operator.cpp +++ b/unittests/dynamics/matrix_operator.cpp @@ -12,7 +12,8 @@ TEST(OperatorExpressions, checkMatrixOpsUnary) { auto create = cudaq::matrix_operator::position(0); - utils::checkEqual((-create).to_matrix({{0,2}}), -1.0 * utils::position_matrix(2)); + utils::checkEqual((-create).to_matrix({{0, 2}}), + -1.0 * utils::position_matrix(2)); } TEST(OperatorExpressions, checkPreBuiltMatrixOps) { @@ -101,20 +102,24 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { std::unordered_map dimensions = { {0, level_count + 1}, {1, level_count + 2}, {3, level_count}}; - { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::position_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + { + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::position_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } // op 0: // momentum level+1 on 0 @@ -125,31 +130,28 @@ TEST(OperatorExpressions, checkCustomMatrixOps) { auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 3}); - auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), - utils::position_matrix(level_count + 2)); - auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 2), - utils::number_matrix(level_count)); + auto matrix0 = cudaq::kronecker(utils::momentum_matrix(level_count + 1), + utils::position_matrix(level_count + 2)); + auto matrix1 = cudaq::kronecker(utils::position_matrix(level_count + 2), + utils::number_matrix(level_count)); std::vector product_matrices = { utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1) - }; - std::vector product_reverse_matrices = { + utils::position_matrix(level_count + 2) * + utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; + std::vector product_reverse_matrices = { utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1) - }; - std::vector sum_matrices_term0 = { - utils::id_matrix(level_count), - utils::position_matrix(level_count + 2), - utils::momentum_matrix(level_count + 1) - }; - std::vector sum_matrices_term1 = { + utils::position_matrix(level_count + 2) * + utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; + std::vector sum_matrices_term0 = { + utils::id_matrix(level_count), utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 1)}; + std::vector sum_matrices_term1 = { utils::number_matrix(level_count), utils::position_matrix(level_count + 2), - utils::id_matrix(level_count + 1) - }; + utils::id_matrix(level_count + 1)}; auto expected_product = cudaq::kronecker(product_matrices.begin(), product_matrices.end()); @@ -269,8 +271,7 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); auto got_matrix = sum.to_matrix({{degree_index, level_count}}); auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}); - auto want_matrix = - utils::momentum_matrix(level_count) + scaled_identity; + auto want_matrix = utils::momentum_matrix(level_count) + scaled_identity; auto want_reverse_matrix = scaled_identity + utils::momentum_matrix(level_count); utils::checkEqual(want_matrix, got_matrix); @@ -383,8 +384,10 @@ TEST(OperatorExpressions, checkMatrixOpsWithScalars) { ASSERT_TRUE(reverse.degrees() == want_degrees); auto scaled_identity = const_scale_factor * utils::id_matrix(level_count); - auto got_matrix = product.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); - auto got_reverse_matrix = reverse.to_matrix({{degree_index, level_count}}, {{"value", const_scale_factor}}); + auto got_matrix = product.to_matrix({{degree_index, level_count}}, + {{"value", const_scale_factor}}); + auto got_reverse_matrix = reverse.to_matrix( + {{degree_index, level_count}}, {{"value", const_scale_factor}}); auto want_matrix = utils::position_matrix(level_count) * scaled_identity; auto want_reverse_matrix = scaled_identity * utils::position_matrix(level_count); @@ -422,9 +425,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto sum = self + other; ASSERT_TRUE(sum.num_terms() == 2); - auto annihilate_full = - cudaq::kronecker(utils::id_matrix(level_count), - utils::momentum_matrix(level_count)); + auto annihilate_full = cudaq::kronecker( + utils::id_matrix(level_count), utils::momentum_matrix(level_count)); auto create_full = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = sum.to_matrix({{0, level_count}, {1, level_count}}); @@ -454,9 +456,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { auto sum = self - other; ASSERT_TRUE(sum.num_terms() == 2); - auto annihilate_full = - cudaq::kronecker(utils::id_matrix(level_count), - utils::momentum_matrix(level_count)); + auto annihilate_full = cudaq::kronecker( + utils::id_matrix(level_count), utils::momentum_matrix(level_count)); auto create_full = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = sum.to_matrix(dimensions); @@ -492,9 +493,8 @@ TEST(OperatorExpressions, checkMatrixOpsSimpleArithmetics) { std::vector want_degrees = {1, 0}; ASSERT_TRUE(product.degrees() == want_degrees); - auto annihilate_full = - cudaq::kronecker(utils::id_matrix(level_count), - utils::momentum_matrix(level_count)); + auto annihilate_full = cudaq::kronecker( + utils::id_matrix(level_count), utils::momentum_matrix(level_count)); auto create_full = cudaq::kronecker(utils::position_matrix(level_count), utils::id_matrix(level_count)); auto got_matrix = product.to_matrix(dimensions); @@ -676,16 +676,20 @@ TEST(OperatorExpressions, checkMatrixOpsDegreeVerification) { std::unordered_map dimensions = {{0, 2}, {1, 2}, {2, 3}, {3, 3}}; { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::position_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::position_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); } diff --git a/unittests/dynamics/operator_conversions.cpp b/unittests/dynamics/operator_conversions.cpp index da9b9409df..694e4ad4ed 100644 --- a/unittests/dynamics/operator_conversions.cpp +++ b/unittests/dynamics/operator_conversions.cpp @@ -32,13 +32,14 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { utils::checkEqual(got, expected); }; - auto checkProductEquals = [dimensions, parameters]( - cudaq::product_operator prod, - cudaq::matrix_2 expected, int expected_num_terms = 2) { - auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; + auto checkProductEquals = + [dimensions, + parameters](cudaq::product_operator prod, + cudaq::matrix_2 expected, int expected_num_terms = 2) { + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; // `elementary + elementary` { @@ -86,15 +87,25 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { // `elementary * elementary` { - checkProductEquals(matrix_elementary * matrix_elementary, matrix_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * spin_elementary, spin_elementary_expected * spin_elementary_expected, 1); - checkProductEquals(boson_elementary * boson_elementary, boson_elementary_expected * boson_elementary_expected, 1); - checkProductEquals(matrix_elementary * spin_elementary, matrix_elementary_expected * spin_elementary_expected); - checkProductEquals(spin_elementary * matrix_elementary, spin_elementary_expected * matrix_elementary_expected); - checkProductEquals(matrix_elementary * boson_elementary, matrix_elementary_expected * boson_elementary_expected); - checkProductEquals(boson_elementary * matrix_elementary, boson_elementary_expected * matrix_elementary_expected); - checkProductEquals(spin_elementary * boson_elementary, spin_elementary_expected * boson_elementary_expected); - checkProductEquals(boson_elementary * spin_elementary, boson_elementary_expected * spin_elementary_expected); + checkProductEquals(matrix_elementary * matrix_elementary, + matrix_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * spin_elementary, + spin_elementary_expected * spin_elementary_expected, 1); + checkProductEquals(boson_elementary * boson_elementary, + boson_elementary_expected * boson_elementary_expected, + 1); + checkProductEquals(matrix_elementary * spin_elementary, + matrix_elementary_expected * spin_elementary_expected); + checkProductEquals(spin_elementary * matrix_elementary, + spin_elementary_expected * matrix_elementary_expected); + checkProductEquals(matrix_elementary * boson_elementary, + matrix_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * matrix_elementary, + boson_elementary_expected * matrix_elementary_expected); + checkProductEquals(spin_elementary * boson_elementary, + spin_elementary_expected * boson_elementary_expected); + checkProductEquals(boson_elementary * spin_elementary, + boson_elementary_expected * spin_elementary_expected); } // `elementary *= elementary` @@ -106,11 +117,14 @@ TEST(OperatorExpressions, checkElementaryOpsConversions) { auto spin_product = cudaq::product_operator(spin_elementary); spin_product *= spin_elementary; - checkProductEquals(spin_product, spin_elementary_expected * spin_elementary_expected, 1); + checkProductEquals(spin_product, + spin_elementary_expected * spin_elementary_expected, 1); auto boson_product = cudaq::product_operator(boson_elementary); boson_product *= boson_elementary; - checkProductEquals(boson_product, boson_elementary_expected * boson_elementary_expected, 1); + checkProductEquals(boson_product, + boson_elementary_expected * boson_elementary_expected, + 1); matrix_product = cudaq::product_operator(matrix_elementary); matrix_product *= spin_elementary; @@ -150,13 +164,14 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { utils::checkEqual(got, expected); }; - auto checkProductEquals = [dimensions, parameters]( - cudaq::product_operator prod, - cudaq::matrix_2 expected, int expected_num_terms = 4) { - auto got = prod.to_matrix(dimensions, parameters); - ASSERT_TRUE(prod.num_terms() == expected_num_terms); - utils::checkEqual(got, expected); - }; + auto checkProductEquals = + [dimensions, + parameters](cudaq::product_operator prod, + cudaq::matrix_2 expected, int expected_num_terms = 4) { + auto got = prod.to_matrix(dimensions, parameters); + ASSERT_TRUE(prod.num_terms() == expected_num_terms); + utils::checkEqual(got, expected); + }; // `product + product` { @@ -204,15 +219,24 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { // `product * product` { - checkProductEquals(matrix_product * matrix_product, matrix_product_expected * matrix_product_expected); - checkProductEquals(spin_product * spin_product, spin_product_expected * spin_product_expected, 2); - checkProductEquals(boson_product * boson_product, boson_product_expected * boson_product_expected, 2); - checkProductEquals(matrix_product * spin_product, matrix_product_expected * spin_product_expected); - checkProductEquals(spin_product * matrix_product, spin_product_expected * matrix_product_expected); - checkProductEquals(matrix_product * boson_product, matrix_product_expected * boson_product_expected); - checkProductEquals(boson_product * matrix_product, boson_product_expected * matrix_product_expected); - checkProductEquals(spin_product * boson_product, spin_product_expected * boson_product_expected); - checkProductEquals(boson_product * spin_product, boson_product_expected * spin_product_expected); + checkProductEquals(matrix_product * matrix_product, + matrix_product_expected * matrix_product_expected); + checkProductEquals(spin_product * spin_product, + spin_product_expected * spin_product_expected, 2); + checkProductEquals(boson_product * boson_product, + boson_product_expected * boson_product_expected, 2); + checkProductEquals(matrix_product * spin_product, + matrix_product_expected * spin_product_expected); + checkProductEquals(spin_product * matrix_product, + spin_product_expected * matrix_product_expected); + checkProductEquals(matrix_product * boson_product, + matrix_product_expected * boson_product_expected); + checkProductEquals(boson_product * matrix_product, + boson_product_expected * matrix_product_expected); + checkProductEquals(spin_product * boson_product, + spin_product_expected * boson_product_expected); + checkProductEquals(boson_product * spin_product, + boson_product_expected * spin_product_expected); } // `product *= product` @@ -224,11 +248,13 @@ TEST(OperatorExpressions, checkProductOperatorConversions) { auto spin_product_0 = spin_product; spin_product_0 *= spin_product; - checkProductEquals(spin_product_0, spin_product_expected * spin_product_expected, 2); + checkProductEquals(spin_product_0, + spin_product_expected * spin_product_expected, 2); auto boson_product_0 = boson_product; boson_product_0 *= boson_product; - checkProductEquals(boson_product_0, boson_product_expected * boson_product_expected, 2); + checkProductEquals(boson_product_0, + boson_product_expected * boson_product_expected, 2); matrix_product_0 = matrix_product; matrix_product_0 *= spin_product; diff --git a/unittests/dynamics/operator_sum.cpp b/unittests/dynamics/operator_sum.cpp index ccfd6cd11b..2685b99a94 100644 --- a/unittests/dynamics/operator_sum.cpp +++ b/unittests/dynamics/operator_sum.cpp @@ -30,15 +30,15 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { utils::checkEqual(spin_matrix, spin_sum.to_matrix()); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(5); - auto op1 = cudaq::matrix_operator::create(5); + auto op0 = cudaq::matrix_operator::number(5); + auto op1 = cudaq::matrix_operator::parity(5); auto sum = op0 + op1; ASSERT_TRUE(sum.degrees() == want_degrees); auto got_matrix = sum.to_matrix({{5, level_count}}); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); auto want_matrix = matrix0 + matrix1; utils::checkEqual(want_matrix, got_matrix); } @@ -59,8 +59,8 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { utils::checkEqual(spin_matrix, spin_sum.to_matrix()); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(1); + auto op0 = cudaq::matrix_operator::number(0); + auto op1 = cudaq::matrix_operator::parity(1); auto got = op0 + op1; auto got_reverse = op1 + op0; @@ -73,8 +73,8 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { got_reverse.to_matrix({{0, level_count}, {1, level_count}}); auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); auto fullHilbert0 = cudaq::kronecker(identity, matrix0); auto fullHilbert1 = cudaq::kronecker(matrix1, identity); @@ -101,8 +101,8 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { utils::checkEqual(spin_matrix, spin_sum.to_matrix()); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); + auto op0 = cudaq::matrix_operator::number(0); + auto op1 = cudaq::matrix_operator::parity(2); auto got = op0 + op1; auto got_reverse = op1 + op0; @@ -115,8 +115,8 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { got_reverse.to_matrix({{0, level_count}, {2, level_count}}); auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); auto fullHilbert0 = cudaq::kronecker(identity, matrix0); auto fullHilbert1 = cudaq::kronecker(matrix1, identity); @@ -144,8 +144,8 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); + auto op0 = cudaq::matrix_operator::number(0); + auto op1 = cudaq::matrix_operator::parity(2); auto got = op0 + op1; auto got_reverse = op1 + op0; @@ -159,8 +159,8 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { auto got_matrix_reverse = got_reverse.to_matrix(dimensions); auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::number_matrix(level_count); + auto matrix1 = utils::parity_matrix(level_count); std::vector matrices_0 = {identity, matrix0}; std::vector matrices_1 = {matrix1, identity}; @@ -187,157 +187,19 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { // matrix operator against constant { - auto op = cudaq::matrix_operator::annihilate(0); + auto op = cudaq::matrix_operator::parity(0); auto scalar_op = cudaq::scalar_operator(value_0); auto sum = scalar_op + op; auto reverse = op + scalar_op; std::vector want_degrees = {0}; - auto op_matrix = utils::annihilate_matrix(2); + auto op_matrix = utils::parity_matrix(2); auto scalar_matrix = value_0 * utils::id_matrix(2); - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::number(5); - auto op1 = cudaq::matrix_operator::parity(5); - - auto sum = op0 + op1; - ASSERT_TRUE(sum.degrees() == want_degrees); - - auto got_matrix = sum.to_matrix({{5, level_count}}); - auto matrix0 = utils::number_matrix(level_count); - auto matrix1 = utils::parity_matrix(level_count); - auto want_matrix = matrix0 + matrix1; - utils::checkEqual(want_matrix, got_matrix); - } - } - - // Different degrees of freedom. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(1); - auto spin_sum = spin0 + spin1; - - std::vector want_degrees = {1, 0}; - auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + - cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::number(0); - auto op1 = cudaq::matrix_operator::parity(1); - - auto got = op0 + op1; - auto got_reverse = op1 + op0; - - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::number_matrix(level_count); - auto matrix1 = utils::parity_matrix(level_count); - - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 + fullHilbert1; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix, got_matrix_reverse); - } - } - - // Different degrees of freedom, non-consecutive. - // Should produce the same matrices as the above test. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_sum = spin0 + spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + - cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::number(0); - auto op1 = cudaq::matrix_operator::parity(2); - - auto got = op0 + op1; - auto got_reverse = op1 + op0; - - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::number_matrix(level_count); - auto matrix1 = utils::parity_matrix(level_count); - - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 + fullHilbert1; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix, got_matrix_reverse); - } - } - - // Different degrees of freedom, non-consecutive but all dimensions - // provided. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_sum = spin0 + spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::id_matrix(2), utils::PauliX_matrix()) + - cudaq::kronecker(utils::PauliZ_matrix(), utils::id_matrix(2)); - std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; - - ASSERT_TRUE(spin_sum.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_sum.to_matrix(dimensions)); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::number(0); - auto op1 = cudaq::matrix_operator::parity(2); - - auto got = op0 + op1; - auto got_reverse = op1 + op0; - - std::vector want_degrees = {2, 0}; - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - dimensions = {{0, level_count},{1, level_count},{2, level_count}}; - auto got_matrix = got.to_matrix(dimensions); - auto got_matrix_reverse = got_reverse.to_matrix(dimensions); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::number_matrix(level_count); - auto matrix1 = utils::parity_matrix(level_count); - std::vector matrices_0 = {identity, matrix0}; - std::vector matrices_1 = {matrix1, identity}; - - auto fullHilbert0 = cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - auto fullHilbert1 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto want_matrix = fullHilbert0 + fullHilbert1; - auto want_matrix_reverse = fullHilbert1 + fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(got_matrix, want_matrix); - } - } + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); } // spin operator against constant @@ -347,84 +209,25 @@ TEST(OperatorExpressions, checkOperatorSumBasics) { auto sum = scalar_op + op; auto reverse = op + scalar_op; - // matrix operator against constant - { - auto op = cudaq::matrix_operator::parity(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::parity_matrix(2); - auto scalar_matrix = value_0 * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{0, 2}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{0, 2}})); - } - - // spin operator against constant - { - auto op = cudaq::spin_operator::x(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::PauliX_matrix(); - auto scalar_matrix = value_0 * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); - } - - // matrix operator against constant from lambda - { - auto op = cudaq::matrix_operator::parity(1); - auto scalar_op = cudaq::scalar_operator(function); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::parity_matrix(2); - auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); - } + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + auto scalar_matrix = value_0 * utils::id_matrix(2); - // spin operator against constant from lambda - { - auto op = cudaq::spin_operator::x(1); - auto scalar_op = cudaq::scalar_operator(function); - auto sum = scalar_op + op; - auto reverse = op + scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::PauliX_matrix(); - auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); - - ASSERT_TRUE(sum.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix({{1, 2}}, {{"value", 0.3}})); - utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); - } + ASSERT_TRUE(sum.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(scalar_matrix + op_matrix, sum.to_matrix()); + utils::checkEqual(scalar_matrix + op_matrix, reverse.to_matrix()); } // matrix operator against constant from lambda { - auto op = cudaq::matrix_operator::annihilate(1); + auto op = cudaq::matrix_operator::parity(1); auto scalar_op = cudaq::scalar_operator(function); auto sum = scalar_op + op; auto reverse = op + scalar_op; std::vector want_degrees = {1}; - auto op_matrix = utils::annihilate_matrix(2); + auto op_matrix = utils::parity_matrix(2); auto scalar_matrix = scalar_op.evaluate({{"value", 0.3}}) * utils::id_matrix(2); @@ -492,8 +295,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + std::complex` { - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto sum = original + value; auto reverse = value + original; @@ -544,8 +347,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum + scalar_operator` { level_count = 2; - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto sum = original + cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) + original; @@ -629,8 +432,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - std::complex` { - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto difference = original - value; auto reverse = value - original; @@ -659,8 +462,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum - scalar_operator` { - auto original = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto original = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto difference = original - cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) - original; @@ -689,8 +492,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * double` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto product = sum * double_value; auto reverse = double_value * sum; @@ -729,8 +532,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * std::complex` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto product = sum * value; auto reverse = value * sum; @@ -767,8 +570,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum * scalar_operator` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto product = sum * cudaq::scalar_operator(value); auto reverse = cudaq::scalar_operator(value) * sum; @@ -920,8 +723,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum *= scalar_operator` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::momentum(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::momentum(2); sum *= cudaq::scalar_operator(value); @@ -935,8 +738,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { {{0, level_count}, {1, level_count}, {2, level_count + 1}}); std::vector matrices_1 = { - utils::id_matrix(level_count + 1), - utils::parity_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; std::vector matrices_2 = { utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); @@ -950,8 +752,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum += double` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); sum += double_value; @@ -1039,8 +841,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= double` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); sum -= double_value; @@ -1085,8 +887,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { // `operator_sum -= scalar_operator` { - auto sum = cudaq::matrix_operator::number(1) + - cudaq::matrix_operator::identity(2); + auto sum = + cudaq::matrix_operator::number(1) + cudaq::matrix_operator::identity(2); sum -= cudaq::scalar_operator(value); @@ -1098,8 +900,7 @@ TEST(OperatorExpressions, checkOperatorSumAgainstScalars) { std::vector matrices_1 = { utils::id_matrix(level_count + 1), utils::number_matrix(level_count)}; std::vector matrices_2 = { - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto matrix0 = cudaq::kronecker(matrices_1.begin(), matrices_1.end()); auto matrix1 = cudaq::kronecker(matrices_2.begin(), matrices_2.end()); auto scaled_identity = @@ -1142,10 +943,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum += product_operator` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::number(1); - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); sum += product; @@ -1154,22 +955,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), + utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::number_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { utils::parity_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1184,10 +981,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum -= product_operator` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::number(1); - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); sum -= product; @@ -1196,22 +993,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), + utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::number_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { utils::parity_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1226,10 +1019,10 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { // `operator_sum *= product_operator` { - auto product = cudaq::matrix_operator::number(0) * - cudaq::matrix_operator::number(1); - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto product = + cudaq::matrix_operator::number(0) * cudaq::matrix_operator::number(1); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); sum *= product; @@ -1241,22 +1034,18 @@ TEST(OperatorExpressions, checkOperatorSumAgainstProduct) { auto got_matrix = sum.to_matrix( {{0, level_count}, {1, level_count + 1}, {2, level_count + 2}}); std::vector matrices_0_0 = { - utils::id_matrix(level_count + 2), - utils::id_matrix(level_count + 1), + utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::number_matrix(level_count)}; std::vector matrices_0_1 = { utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; std::vector matrices_1_1 = { utils::parity_matrix(level_count + 2), - utils::id_matrix(level_count + 1), - utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * @@ -1276,8 +1065,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum + operator_sum` { - auto sum_0 = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum_0 = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::number(1) + cudaq::matrix_operator::parity(3); @@ -1297,10 +1086,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1308,10 +1096,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { matrices_1_0 = { utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; - matrices_1_1 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_1_1 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_2 = {utils::parity_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1331,8 +1118,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum - operator_sum` { - auto sum_0 = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::position(2); + auto sum_0 = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::position(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::number(1) + cudaq::matrix_operator::momentum(3); @@ -1352,10 +1139,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::position_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1363,10 +1149,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { matrices_1_0 = { utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; - matrices_1_1 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_1_1 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_2 = {utils::momentum_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1386,8 +1171,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum * operator_sum` { - auto sum_0 = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum_0 = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::number(1) + cudaq::matrix_operator::parity(3); @@ -1418,10 +1203,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1429,10 +1213,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { matrices_1_0 = { utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; - matrices_1_1 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_1_1 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_2 = {utils::parity_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1454,8 +1237,8 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { // `operator_sum *= operator_sum` { - auto sum = cudaq::matrix_operator::parity(1) + - cudaq::matrix_operator::parity(2); + auto sum = + cudaq::matrix_operator::parity(1) + cudaq::matrix_operator::parity(2); auto sum_1 = cudaq::matrix_operator::parity(0) + cudaq::matrix_operator::number(1) + cudaq::matrix_operator::parity(3); @@ -1477,10 +1260,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { std::vector matrices_1_1; std::vector matrices_1_2; - matrices_0_0 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::parity_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_0_0 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::parity_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_0_1 = {utils::id_matrix(level_count + 3), utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1488,10 +1270,9 @@ TEST(OperatorExpressions, checkOperatorSumAgainstOperatorSum) { matrices_1_0 = { utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), utils::parity_matrix(level_count)}; - matrices_1_1 = {utils::id_matrix(level_count + 3), - utils::id_matrix(level_count + 2), - utils::number_matrix(level_count + 1), - utils::id_matrix(level_count)}; + matrices_1_1 = { + utils::id_matrix(level_count + 3), utils::id_matrix(level_count + 2), + utils::number_matrix(level_count + 1), utils::id_matrix(level_count)}; matrices_1_2 = {utils::parity_matrix(level_count + 3), utils::id_matrix(level_count + 2), utils::id_matrix(level_count + 1), @@ -1517,20 +1298,24 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { {2, level_count}, {3, level_count + 3}}; - { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::parity_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + { + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::parity_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); @@ -1542,9 +1327,8 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { std::vector matrices_0 = { utils::id_matrix(level_count), utils::position_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; - std::vector matrices_1 = { - utils::number_matrix(level_count), - utils::parity_matrix(level_count + 2), + std::vector matrices_1 = { + utils::number_matrix(level_count), utils::parity_matrix(level_count + 2), utils::id_matrix(level_count + 1)}; auto sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); @@ -1567,24 +1351,23 @@ TEST(OperatorExpressions, checkCustomOperatorSum) { difference = op0 - op1; difference_reverse = op1 - op0; - matrices_0 = { - utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count), - utils::id_matrix(level_count + 1)}; - matrices_1 = { - utils::id_matrix(level_count + 3), - utils::parity_matrix(level_count), - utils::number_matrix(level_count + 1)}; - sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - diff_reverse_expected = cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); + matrices_0 = {utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count), + utils::id_matrix(level_count + 1)}; + matrices_1 = {utils::id_matrix(level_count + 3), + utils::parity_matrix(level_count), + utils::number_matrix(level_count + 1)}; + sum_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) + + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_expected = cudaq::kronecker(matrices_0.begin(), matrices_0.end()) - + cudaq::kronecker(matrices_1.begin(), matrices_1.end()); + diff_reverse_expected = + cudaq::kronecker(matrices_1.begin(), matrices_1.end()) - + cudaq::kronecker(matrices_0.begin(), matrices_0.end()); utils::checkEqual(sum.to_matrix(dimensions), sum_expected); utils::checkEqual(sum_reverse.to_matrix(dimensions), sum_expected); utils::checkEqual(difference.to_matrix(dimensions), diff_expected); utils::checkEqual(difference_reverse.to_matrix(dimensions), diff_reverse_expected); -} +} \ No newline at end of file diff --git a/unittests/dynamics/product_operator.cpp b/unittests/dynamics/product_operator.cpp index 13591f2401..985d614d33 100644 --- a/unittests/dynamics/product_operator.cpp +++ b/unittests/dynamics/product_operator.cpp @@ -32,8 +32,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { utils::checkEqual(spin_matrix, spin_prod.to_matrix()); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(5); - auto op1 = cudaq::matrix_operator::create(5); + auto op0 = cudaq::matrix_operator::position(5); + auto op1 = cudaq::matrix_operator::momentum(5); auto got = op0 * op1; utils::assert_product_equal(got, 1., @@ -41,8 +41,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { ASSERT_TRUE(got.degrees() == want_degrees); auto got_matrix = got.to_matrix({{5, level_count}}); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); auto want_matrix = matrix0 * matrix1; utils::checkEqual(want_matrix, got_matrix); } @@ -62,8 +62,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { utils::checkEqual(spin_matrix, spin_prod.to_matrix()); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(1); + auto op0 = cudaq::matrix_operator::position(0); + auto op1 = cudaq::matrix_operator::momentum(1); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -76,8 +76,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { got_reverse.to_matrix({{0, level_count}, {1, level_count}}); auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); auto fullHilbert0 = cudaq::kronecker(identity, matrix0); auto fullHilbert1 = cudaq::kronecker(matrix1, identity); @@ -104,8 +104,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { utils::checkEqual(spin_matrix, spin_prod.to_matrix()); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); + auto op0 = cudaq::matrix_operator::position(0); + auto op1 = cudaq::matrix_operator::momentum(2); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -118,8 +118,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { got_reverse.to_matrix({{0, level_count}, {2, level_count}}); auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); auto fullHilbert0 = cudaq::kronecker(identity, matrix0); auto fullHilbert1 = cudaq::kronecker(matrix1, identity); @@ -147,8 +147,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::annihilate(0); - auto op1 = cudaq::matrix_operator::create(2); + auto op0 = cudaq::matrix_operator::position(0); + auto op1 = cudaq::matrix_operator::momentum(2); cudaq::product_operator got = op0 * op1; cudaq::product_operator got_reverse = op1 * op0; @@ -162,8 +162,8 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { auto got_matrix_reverse = got_reverse.to_matrix(dimensions); auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::annihilate_matrix(level_count); - auto matrix1 = utils::create_matrix(level_count); + auto matrix0 = utils::position_matrix(level_count); + auto matrix1 = utils::momentum_matrix(level_count); std::vector matrices_0; std::vector matrices_1; @@ -193,161 +193,18 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { // matrix operator against constant { - auto op = cudaq::matrix_operator::annihilate(0); + auto op = cudaq::matrix_operator::position(0); auto scalar_op = cudaq::scalar_operator(value_0); auto product = scalar_op * op; auto reverse = op * scalar_op; std::vector want_degrees = {0}; - auto op_matrix = utils::annihilate_matrix(2); + auto op_matrix = utils::position_matrix(2); - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::position(5); - auto op1 = cudaq::matrix_operator::momentum(5); - - auto got = op0 * op1; - utils::assert_product_equal(got, 1., {op0.get_terms()[0], op1.get_terms()[0]}); - ASSERT_TRUE(got.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{5, level_count}}); - auto matrix0 = utils::position_matrix(level_count); - auto matrix1 = utils::momentum_matrix(level_count); - auto want_matrix = matrix0 * matrix1; - utils::checkEqual(want_matrix, got_matrix); - } - } - - // Different degrees of freedom. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(1); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {1, 0}; - auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::position(0); - auto op1 = cudaq::matrix_operator::momentum(1); - - cudaq::product_operator got = op0 * op1; - cudaq::product_operator got_reverse = op1 * op0; - - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{0, level_count}, {1, level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0, level_count}, {1, level_count}}); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::position_matrix(level_count); - auto matrix1 = utils::momentum_matrix(level_count); - - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 * fullHilbert1; - auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - } - - // Different degrees of freedom, non-consecutive. - // Should produce the same matrices as the above test. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix()); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::position(0); - auto op1 = cudaq::matrix_operator::momentum(2); - - cudaq::product_operator got = op0 * op1; - cudaq::product_operator got_reverse = op1 * op0; - - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - auto got_matrix = got.to_matrix({{0,level_count},{2,level_count}}); - auto got_matrix_reverse = got_reverse.to_matrix({{0,level_count},{2,level_count}}); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::position_matrix(level_count); - auto matrix1 = utils::momentum_matrix(level_count); - - auto fullHilbert0 = cudaq::kronecker(identity, matrix0); - auto fullHilbert1 = cudaq::kronecker(matrix1, identity); - auto want_matrix = fullHilbert0 * fullHilbert1; - auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(want_matrix_reverse, got_matrix_reverse); - } - } - - // Different degrees of freedom, non-consecutive but all dimensions - // provided. - { - auto spin0 = cudaq::spin_operator::x(0); - auto spin1 = cudaq::spin_operator::z(2); - auto spin_prod = spin0 * spin1; - - std::vector want_degrees = {2, 0}; - auto spin_matrix = cudaq::kronecker(utils::PauliZ_matrix(), utils::PauliX_matrix()); - std::unordered_map dimensions = {{0, 2},{1, 2},{2, 2}}; - - ASSERT_TRUE(spin_prod.degrees() == want_degrees); - utils::checkEqual(spin_matrix, spin_prod.to_matrix(dimensions)); - - for (auto level_count : levels) { - auto op0 = cudaq::matrix_operator::position(0); - auto op1 = cudaq::matrix_operator::momentum(2); - - cudaq::product_operator got = op0 * op1; - cudaq::product_operator got_reverse = op1 * op0; - - std::vector want_degrees = {2, 0}; - ASSERT_TRUE(got.degrees() == want_degrees); - ASSERT_TRUE(got_reverse.degrees() == want_degrees); - - dimensions = {{0, level_count},{1, level_count},{2, level_count}}; - auto got_matrix = got.to_matrix(dimensions); - auto got_matrix_reverse = got_reverse.to_matrix(dimensions); - - auto identity = utils::id_matrix(level_count); - auto matrix0 = utils::position_matrix(level_count); - auto matrix1 = utils::momentum_matrix(level_count); - - std::vector matrices_0; - std::vector matrices_1; - matrices_0 = {identity, matrix0}; - matrices_1 = {matrix1, identity}; - - auto fullHilbert0 = - cudaq::kronecker(matrices_0.begin(), matrices_0.end()); - auto fullHilbert1 = - cudaq::kronecker(matrices_1.begin(), matrices_1.end()); - auto want_matrix = fullHilbert0 * fullHilbert1; - auto want_matrix_reverse = fullHilbert1 * fullHilbert0; - - utils::checkEqual(want_matrix, got_matrix); - utils::checkEqual(got_matrix, want_matrix); - } - } + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); } // spin operator against constant @@ -357,80 +214,24 @@ TEST(OperatorExpressions, checkProductOperatorBasics) { auto product = scalar_op * op; auto reverse = op * scalar_op; - // matrix operator against constant - { - auto op = cudaq::matrix_operator::position(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::position_matrix(2); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(value_0 * op_matrix, product.to_matrix({{0, 2}})); - utils::checkEqual(value_0 * op_matrix, reverse.to_matrix({{0, 2}})); - } - - // spin operator against constant - { - auto op = cudaq::spin_operator::x(0); - auto scalar_op = cudaq::scalar_operator(value_0); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {0}; - auto op_matrix = utils::PauliX_matrix(); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(value_0 * op_matrix, product.to_matrix()); - utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); - } - - // matrix operator against constant from lambda - { - auto op = cudaq::matrix_operator::position(1); - auto scalar_op = cudaq::scalar_operator(function); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::position_matrix(2); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({{1, 2}}, {{"value", 0.3}})); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({{1, 2}}, {{"value", 0.3}})); - } - - // spin operator against constant from lambda - { - auto op = cudaq::spin_operator::x(1); - auto scalar_op = cudaq::scalar_operator(function); - auto product = scalar_op * op; - auto reverse = op * scalar_op; - - std::vector want_degrees = {1}; - auto op_matrix = utils::PauliX_matrix(); - - ASSERT_TRUE(product.degrees() == want_degrees); - ASSERT_TRUE(reverse.degrees() == want_degrees); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, product.to_matrix({}, {{"value", 0.3}})); - utils::checkEqual(scalar_op.evaluate({{"value", 0.3}}) * op_matrix, reverse.to_matrix({}, {{"value", 0.3}})); - } + std::vector want_degrees = {0}; + auto op_matrix = utils::PauliX_matrix(); + + ASSERT_TRUE(product.degrees() == want_degrees); + ASSERT_TRUE(reverse.degrees() == want_degrees); + utils::checkEqual(value_0 * op_matrix, product.to_matrix()); + utils::checkEqual(value_0 * op_matrix, reverse.to_matrix()); } // matrix operator against constant from lambda { - auto op = cudaq::matrix_operator::annihilate(1); + auto op = cudaq::matrix_operator::position(1); auto scalar_op = cudaq::scalar_operator(function); auto product = scalar_op * op; auto reverse = op * scalar_op; std::vector want_degrees = {1}; - auto op_matrix = utils::annihilate_matrix(2); + auto op_matrix = utils::position_matrix(2); ASSERT_TRUE(product.degrees() == want_degrees); ASSERT_TRUE(reverse.degrees() == want_degrees); @@ -1077,8 +878,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator - product_operator` { - auto term_0 = cudaq::matrix_operator::position(0) * - cudaq::matrix_operator::number(1); + auto term_0 = + cudaq::matrix_operator::position(0) * cudaq::matrix_operator::number(1); auto term_1 = cudaq::matrix_operator::momentum(1) * cudaq::matrix_operator::momentum(2); @@ -1158,8 +959,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { { auto term_0 = cudaq::matrix_operator::position(0) * cudaq::matrix_operator::position(1); - auto term_1 = cudaq::matrix_operator::momentum(1) * - cudaq::matrix_operator::parity(2); + auto term_1 = + cudaq::matrix_operator::momentum(1) * cudaq::matrix_operator::parity(2); auto product = term_0 * term_1; @@ -1241,8 +1042,8 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { // `product_operator *= product_operator` { - auto term_0 = cudaq::matrix_operator::position(0) * - cudaq::matrix_operator::number(1); + auto term_0 = + cudaq::matrix_operator::position(0) * cudaq::matrix_operator::number(1); auto term_1 = cudaq::matrix_operator::momentum(1) * cudaq::matrix_operator::position(2); @@ -1278,7 +1079,9 @@ TEST(OperatorExpressions, checkProductOperatorAgainstProduct) { cudaq::kronecker(matrices_1_1.begin(), matrices_1_1.end()); auto want_matrix = term_0_matrix * term_1_matrix; - auto term1_only_matrix = cudaq::kronecker(utils::position_matrix(level_count + 1), utils::momentum_matrix(level_count)); + auto term1_only_matrix = + cudaq::kronecker(utils::position_matrix(level_count + 1), + utils::momentum_matrix(level_count)); utils::checkEqual(want_matrix, got_matrix); utils::checkEqual(term1_only_matrix, term_1.to_matrix(dimensions)); } @@ -1349,15 +1152,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), utils::position_matrix(level_count)}; std::vector matrices_0_1 = { - utils::id_matrix(level_count + 1), - utils::position_matrix(level_count), + utils::id_matrix(level_count + 1), utils::position_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), - utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::momentum_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::momentum_matrix(level_count + 1), - utils::id_matrix(level_count), utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1428,15 +1230,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), utils::position_matrix(level_count)}; std::vector matrices_0_1 = { - utils::id_matrix(level_count + 1), - utils::position_matrix(level_count), + utils::id_matrix(level_count + 1), utils::position_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), - utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::momentum_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::momentum_matrix(level_count + 1), - utils::id_matrix(level_count), utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1508,15 +1309,14 @@ TEST(OperatorExpressions, checkProductOperatorAgainstOperatorSum) { utils::id_matrix(level_count + 1), utils::id_matrix(level_count), utils::position_matrix(level_count)}; std::vector matrices_0_1 = { - utils::id_matrix(level_count + 1), - utils::position_matrix(level_count), + utils::id_matrix(level_count + 1), utils::position_matrix(level_count), utils::id_matrix(level_count)}; std::vector matrices_1_0 = { - utils::id_matrix(level_count + 1), - utils::momentum_matrix(level_count), utils::id_matrix(level_count)}; + utils::id_matrix(level_count + 1), utils::momentum_matrix(level_count), + utils::id_matrix(level_count)}; std::vector matrices_1_1 = { - utils::momentum_matrix(level_count + 1), - utils::id_matrix(level_count), utils::id_matrix(level_count)}; + utils::momentum_matrix(level_count + 1), utils::id_matrix(level_count), + utils::id_matrix(level_count)}; auto product_matrix = cudaq::kronecker(matrices_0_0.begin(), matrices_0_0.end()) * cudaq::kronecker(matrices_0_1.begin(), matrices_0_1.end()); @@ -1576,20 +1376,24 @@ TEST(OperatorExpressions, checkCustomProductOps) { {2, level_count}, {3, level_count + 3}}; - { - auto func0 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::position_matrix(dimensions[1]));; - }; - auto func1 = [](const std::vector &dimensions, - const std::unordered_map> &_none) { - return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), - utils::number_matrix(dimensions[1]));; - }; - cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); - cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); - } + { + auto func0 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::position_matrix(dimensions[1])); + ; + }; + auto func1 = + [](const std::vector &dimensions, + const std::unordered_map> &_none) { + return cudaq::kronecker(utils::momentum_matrix(dimensions[0]), + utils::number_matrix(dimensions[1])); + ; + }; + cudaq::matrix_operator::define("custom_op0", {-1, -1}, func0); + cudaq::matrix_operator::define("custom_op1", {-1, -1}, func1); + } auto op0 = cudaq::matrix_operator::instantiate("custom_op0", {0, 1}); auto op1 = cudaq::matrix_operator::instantiate("custom_op1", {1, 2}); @@ -1598,13 +1402,15 @@ TEST(OperatorExpressions, checkCustomProductOps) { std::vector matrices = { utils::number_matrix(level_count), - utils::position_matrix(level_count + 2) * utils::momentum_matrix(level_count + 2), + utils::position_matrix(level_count + 2) * + utils::momentum_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; auto expected = cudaq::kronecker(matrices.begin(), matrices.end()); std::vector matrices_reverse = { utils::number_matrix(level_count), - utils::momentum_matrix(level_count + 2) * utils::position_matrix(level_count + 2), + utils::momentum_matrix(level_count + 2) * + utils::position_matrix(level_count + 2), utils::momentum_matrix(level_count + 1)}; auto expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); @@ -1617,17 +1423,18 @@ TEST(OperatorExpressions, checkCustomProductOps) { product = op0 * op1; reverse = op1 * op0; - matrices = { - utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count) * utils::momentum_matrix(level_count), - utils::number_matrix(level_count + 1)}; - expected = cudaq::kronecker(matrices.begin(), matrices.end()); - - matrices_reverse = { - utils::position_matrix(level_count + 3), - utils::momentum_matrix(level_count) * utils::momentum_matrix(level_count), - utils::number_matrix(level_count + 1)}; - expected_reverse = cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); + matrices = {utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count) * + utils::momentum_matrix(level_count), + utils::number_matrix(level_count + 1)}; + expected = cudaq::kronecker(matrices.begin(), matrices.end()); + + matrices_reverse = {utils::position_matrix(level_count + 3), + utils::momentum_matrix(level_count) * + utils::momentum_matrix(level_count), + utils::number_matrix(level_count + 1)}; + expected_reverse = + cudaq::kronecker(matrices_reverse.begin(), matrices_reverse.end()); utils::checkEqual(product.to_matrix(dimensions), expected); utils::checkEqual(reverse.to_matrix(dimensions), expected_reverse); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp index b097d51cb1..74e94b64d9 100644 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ b/unittests/dynamics/test_cudm_helpers.cpp @@ -15,7 +15,7 @@ // Initialize operator_sum cudaq::operator_sum initialize_operator_sum() { - return cudaq::matrix_operator::create(0) + cudaq::matrix_operator::create(1); + return cudaq::boson_operator::create(0) + cudaq::boson_operator::create(1); } class CuDensityMatHelpersTestFixture : public ::testing::Test { @@ -133,7 +133,9 @@ TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { cudaq::ScalarCallbackFunction scalar_callback_function(callback_function); cudaq::scalar_operator scalar_callback(scalar_callback_function); - auto op_sum = scalar_callback * cudaq::matrix_operator::create(0); + cudaq::product_operator op_sum_1 = + scalar_callback * cudaq::boson_operator::create(0); + cudaq::operator_sum op_sum(op_sum_1); EXPECT_NO_THROW({ auto result = diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index b4fa23cbaf..2ff8a53c1a 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -112,7 +112,9 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { EXPECT_TRUE(castSimState != nullptr); castSimState->initialize_cudm(handle_, dims); - auto op = cudaq::matrix_operator::create(0); + cudaq::product_operator op_1 = + cudaq::boson_operator::create(0); + cudaq::operator_sum op(op_1); auto cudmOp = helper_->convert_to_cudensitymat_operator( {}, op, dims); // Initialize the time stepper @@ -143,7 +145,7 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { EXPECT_TRUE(castSimState != nullptr); castSimState->initialize_cudm(handle_, dims); cudaq::product_operator c_op_0 = - cudaq::matrix_operator::annihilate(0); + cudaq::boson_operator::annihilate(0); cudaq::operator_sum c_op(c_op_0); cudaq::operator_sum zero_op = 0.0 * c_op; auto cudm_lindblad_op = @@ -191,8 +193,9 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { return entry->second; }; - auto op = - cudaq::scalar_operator(function) * cudaq::matrix_operator::create(0); + cudaq::product_operator op_t = + cudaq::scalar_operator(function) * cudaq::boson_operator::create(0); + cudaq::operator_sum op(op_t); auto cudmOp = helper_->convert_to_cudensitymat_operator( params, op, dims); diff --git a/unittests/dynamics/test_evolve_single.cpp b/unittests/dynamics/test_evolve_single.cpp index 02095aed66..73d6b63ed5 100644 --- a/unittests/dynamics/test_evolve_single.cpp +++ b/unittests/dynamics/test_evolve_single.cpp @@ -123,11 +123,11 @@ TEST(EvolveTester, checkDensityMatrixSimple) { TEST(EvolveTester, checkCompositeSystem) { constexpr int cavity_levels = 10; const std::map dims = {{0, 2}, {1, cavity_levels}}; - auto a = cudaq::matrix_operator::annihilate(1); - auto a_dag = cudaq::matrix_operator::create(1); + auto a = cudaq::boson_operator::annihilate(1); + auto a_dag = cudaq::boson_operator::create(1); - auto sm = cudaq::matrix_operator::annihilate(0); - auto sm_dag = cudaq::matrix_operator::create(0); + auto sm = cudaq::boson_operator::annihilate(0); + auto sm_dag = cudaq::boson_operator::create(0); cudaq::product_operator atom_occ_op_t = cudaq::matrix_operator::number(0); @@ -156,9 +156,9 @@ TEST(EvolveTester, checkCompositeSystem) { integrator.dt = 0.001; integrator.order = 4; - auto result = cudaq::evolve_single(hamiltonian, dims, schedule, initialState, - integrator, {}, - {cavity_occ_op, atom_occ_op}, true); + auto result = + cudaq::evolve_single(hamiltonian, dims, schedule, initialState, + integrator, {}, {cavity_occ_op, atom_occ_op}, true); EXPECT_TRUE(result.get_expectation_values().has_value()); EXPECT_EQ(result.get_expectation_values().value().size(), num_steps); @@ -175,11 +175,11 @@ TEST(EvolveTester, checkCompositeSystem) { TEST(EvolveTester, checkCompositeSystemWithCollapse) { constexpr int cavity_levels = 10; const std::map dims = {{0, 2}, {1, cavity_levels}}; - auto a = cudaq::matrix_operator::annihilate(1); - auto a_dag = cudaq::matrix_operator::create(1); + auto a = cudaq::boson_operator::annihilate(1); + auto a_dag = cudaq::boson_operator::create(1); - auto sm = cudaq::matrix_operator::annihilate(0); - auto sm_dag = cudaq::matrix_operator::create(0); + auto sm = cudaq::boson_operator::annihilate(0); + auto sm_dag = cudaq::boson_operator::create(0); cudaq::product_operator atom_occ_op_t = cudaq::matrix_operator::number(0); diff --git a/unittests/dynamics/utils.cpp b/unittests/dynamics/utils.cpp index 394fd24a75..5c0c1926d3 100644 --- a/unittests/dynamics/utils.cpp +++ b/unittests/dynamics/utils.cpp @@ -6,14 +6,16 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ -#include +#include "cudaq/operators.h" #include "cudaq/utils/tensor.h" #include +#include namespace utils { void print(cudaq::matrix_2 mat, std::string name = "") { - if (name != "") std::cout << name << ":" << std::endl; + if (name != "") + std::cout << name << ":" << std::endl; for (std::size_t i = 0; i < mat.get_rows(); i++) { for (std::size_t j = 0; j < mat.get_columns(); j++) std::cout << mat[{i, j}] << " "; diff --git a/unittests/dynamics/utils.h b/unittests/dynamics/utils.h index fcf5c74202..a3accf9c86 100644 --- a/unittests/dynamics/utils.h +++ b/unittests/dynamics/utils.h @@ -6,6 +6,9 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#pragma once + +#include "cudaq/dynamics/matrix_operators.h" #include "cudaq/operators.h" #include "cudaq/utils/tensor.h" From 97f229bfc51a3ad5e3d7315c6193d00dddabf7b4 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Thu, 20 Feb 2025 23:01:29 +0000 Subject: [PATCH 309/311] Fix the order of elemetary op application in a product term: we write left to write but they should be applied right to left Signed-off-by: Thien Nguyen --- runtime/nvqir/cudensitymat/CMakeLists.txt | 1 - .../cudensitymat/CuDensityMatOpConverter.cpp | 4 + .../nvqir/cudensitymat/CuDensityMatUtils.h | 35 + .../nvqir/cudensitymat/cudm_expectation.cpp | 7 +- runtime/nvqir/cudensitymat/cudm_helpers.cpp | 1029 ----------------- runtime/nvqir/cudensitymat/cudm_helpers.h | 121 -- .../nvqir/cudensitymat/cudm_time_stepper.cpp | 1 - unittests/CMakeLists.txt | 1 - unittests/dynamics/test_cudm_expectation.cpp | 14 +- unittests/dynamics/test_cudm_helpers.cpp | 183 --- unittests/dynamics/test_cudm_state.cpp | 1 - unittests/dynamics/test_cudm_time_stepper.cpp | 112 +- .../dynamics/test_runge_kutta_integrator.cpp | 2 - 13 files changed, 142 insertions(+), 1369 deletions(-) create mode 100644 runtime/nvqir/cudensitymat/CuDensityMatUtils.h delete mode 100644 runtime/nvqir/cudensitymat/cudm_helpers.cpp delete mode 100644 runtime/nvqir/cudensitymat/cudm_helpers.h delete mode 100644 unittests/dynamics/test_cudm_helpers.cpp diff --git a/runtime/nvqir/cudensitymat/CMakeLists.txt b/runtime/nvqir/cudensitymat/CMakeLists.txt index 806141d01c..b5f5e20330 100644 --- a/runtime/nvqir/cudensitymat/CMakeLists.txt +++ b/runtime/nvqir/cudensitymat/CMakeLists.txt @@ -28,7 +28,6 @@ get_filename_component(CUDENSITYMAT_INCLUDE_DIR ${CUDENSITYMAT_INC} DIRECTORY) add_library(${LIBRARY_NAME} SHARED CuDensityMatSim.cpp mpi_support.cpp - cudm_helpers.cpp cudm_time_stepper.cpp runge_kutta_integrator.cpp cudm_expectation.cpp diff --git a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp index 87e9f03c32..2be84f6e31 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp @@ -403,6 +403,10 @@ cudaq::dynamics::OpConverter::convertToCudensitymat( throw std::runtime_error("Unhandled type!"); } } + // Note: the order of operator application is the opposite of the writing: + // i.e., ABC means C to be applied first. + std::reverse(elemOps.begin(), elemOps.end()); + std::reverse(allDegrees.begin(), allDegrees.end()); result.emplace_back(std::make_pair( productOp.get_coefficient(), createProductOperatorTerm(elemOps, modeExtents, allDegrees, {}))); diff --git a/runtime/nvqir/cudensitymat/CuDensityMatUtils.h b/runtime/nvqir/cudensitymat/CuDensityMatUtils.h new file mode 100644 index 0000000000..255d2cf77a --- /dev/null +++ b/runtime/nvqir/cudensitymat/CuDensityMatUtils.h @@ -0,0 +1,35 @@ +/****************************************************************-*- C++ -*-**** + * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * + * All rights reserved. * + * * + * This source code and the accompanying materials are made available under * + * the terms of the Apache License 2.0 which accompanies this distribution. * + ******************************************************************************/ + +#pragma once +#include "cudm_error_handling.h" +#include +#include +#include + +namespace cudaq { +namespace dynamics { +// GPU memory management +template +void *createArrayGpu(const std::vector> &cpuArray) { + void *gpuArray{nullptr}; + const std::size_t arraySizeBytes = cpuArray.size() * sizeof(std::complex); + if (arraySizeBytes > 0) { + HANDLE_CUDA_ERROR(cudaMalloc(&gpuArray, arraySizeBytes)); + HANDLE_CUDA_ERROR(cudaMemcpy(gpuArray, + static_cast(cpuArray.data()), + arraySizeBytes, cudaMemcpyHostToDevice)); + } + return gpuArray; +} +inline void destroyArrayGpu(void *gpuArray) { + if (gpuArray) + HANDLE_CUDA_ERROR(cudaFree(gpuArray)); +} +} // namespace dynamics +} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_expectation.cpp b/runtime/nvqir/cudensitymat/cudm_expectation.cpp index d43530e5aa..418d297875 100644 --- a/runtime/nvqir/cudensitymat/cudm_expectation.cpp +++ b/runtime/nvqir/cudensitymat/cudm_expectation.cpp @@ -10,8 +10,7 @@ #include "CuDensityMatContext.h" #include "common/Logger.h" #include "cudm_error_handling.h" -#include "cudm_helpers.h" - +#include "CuDensityMatUtils.h" namespace cudaq { cudm_expectation::cudm_expectation(cudensitymatHandle_t handle, cudensitymatOperator_t op) @@ -54,7 +53,7 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, CUDENSITYMAT_WORKSPACE_SCRATCH, workspaceBuffer, requiredBufferSize)); } - auto *expectationValue_d = cudm_helper::create_array_gpu( + auto *expectationValue_d = cudaq::dynamics::createArrayGpu( std::vector>(1, {0.0, 0.0})); HANDLE_CUDM_ERROR(cudensitymatExpectationCompute( m_handle, m_expectation, time, 0, nullptr, state, expectationValue_d, @@ -63,7 +62,7 @@ std::complex cudm_expectation::compute(cudensitymatState_t state, HANDLE_CUDA_ERROR(cudaMemcpy(&result, expectationValue_d, sizeof(std::complex), cudaMemcpyDefault)); - cudm_helper::destroy_array_gpu(expectationValue_d); + cudaq::dynamics::destroyArrayGpu(expectationValue_d); return result; } } // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.cpp b/runtime/nvqir/cudensitymat/cudm_helpers.cpp deleted file mode 100644 index 3ae86e1565..0000000000 --- a/runtime/nvqir/cudensitymat/cudm_helpers.cpp +++ /dev/null @@ -1,1029 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "cudm_helpers.h" -#include "common/Logger.h" -#include "cudm_error_handling.h" -#include - -using namespace cudaq; - -namespace cudaq { -cudm_helper::cudm_helper(cudensitymatHandle_t handle) : handle(handle) {} - -cudm_helper::~cudm_helper() { - cudaDeviceSynchronize(); - - for (auto term : m_operatorTerms) { - cudensitymatDestroyOperatorTerm(term); - } - for (auto op : m_elementaryOperators) { - cudensitymatDestroyElementaryOperator(op); - } - for (auto *buffer : m_deviceBuffers) { - cudaFree(buffer); - } -} - -struct ScalarCallBackContext { - scalar_operator scalarOp; - std::vector paramNames; - ScalarCallBackContext(const scalar_operator &scalar_op, - const std::vector ¶mNames) - : scalarOp(scalar_op), paramNames(paramNames){}; -}; - -struct TensorCallBackContext { - matrix_operator tensorOp; - std::vector paramNames; - - TensorCallBackContext(const matrix_operator &tensor_op, - const std::vector ¶m_names) - : tensorOp(tensor_op), paramNames(param_names){}; -}; - -cudensitymatWrappedScalarCallback_t -cudm_helper::_wrap_callback(const scalar_operator &scalar_op, - const std::vector ¶mNames) { - if (scalar_op.is_constant()) { - throw std::runtime_error( - "scalar_operator does not have a valid generator function."); - } - - // FIXME: leak - auto *stored_callback_context = - new ScalarCallBackContext(scalar_op, paramNames); - using WrapperFuncType = - int32_t (*)(cudensitymatScalarCallback_t, double, int32_t, const double[], - cudaDataType_t, void *); - - auto wrapper = [](cudensitymatScalarCallback_t callback, double time, - int32_t num_params, const double params[], - cudaDataType_t data_type, void *scalar_storage) -> int32_t { - try { - ScalarCallBackContext *context = - reinterpret_cast(callback); - scalar_operator &stored_op = context->scalarOp; - if (num_params != 2 * context->paramNames.size()) - throw std::runtime_error( - fmt::format("[Internal Error] Invalid number of callback " - "parameters encountered. Expected {} double params " - "representing {} complex values but received {}.", - 2 * context->paramNames.size(), - context->paramNames.size(), num_params)); - - std::unordered_map> param_map; - for (size_t i = 0; i < context->paramNames.size(); ++i) { - param_map[context->paramNames[i]] = - std::complex(params[2 * i], params[2 * i + 1]); - cudaq::debug("Callback param name {}, value {}", context->paramNames[i], - param_map[context->paramNames[i]]); - } - - std::complex result = stored_op.evaluate(param_map); - cudaq::debug("Scalar callback evaluated result = {}", result); - auto *tdCoef = static_cast *>(scalar_storage); - *tdCoef = result; - return CUDENSITYMAT_STATUS_SUCCESS; - } catch (const std::exception &e) { - std::cerr << "Error in scalar callback: " << e.what() << std::endl; - return CUDENSITYMAT_STATUS_INTERNAL_ERROR; - } - }; - - cudensitymatWrappedScalarCallback_t wrappedCallback; - wrappedCallback.callback = - reinterpret_cast(stored_callback_context); - wrappedCallback.wrapper = - reinterpret_cast(static_cast(wrapper)); - return wrappedCallback; -} - -cudensitymatWrappedTensorCallback_t -cudm_helper::_wrap_tensor_callback(const matrix_operator &op, - const std::vector ¶mNames) { - auto *stored_callback_context = new TensorCallBackContext(op, paramNames); - - using WrapperFuncType = int32_t (*)( - cudensitymatTensorCallback_t, cudensitymatElementaryOperatorSparsity_t, - int32_t, const int64_t[], const int32_t[], double, int32_t, - const double[], cudaDataType_t, void *, cudaStream_t); - - auto wrapper = [](cudensitymatTensorCallback_t callback, - cudensitymatElementaryOperatorSparsity_t sparsity, - int32_t num_modes, const int64_t mode_extents[], - const int32_t diagonal_offsets[], double time, - int32_t num_params, const double params[], - cudaDataType_t data_type, void *tensor_storage, - cudaStream_t stream) -> int32_t { - try { - auto *context = reinterpret_cast(callback); - matrix_operator &stored_op = context->tensorOp; - - if (num_modes <= 0) { - std::cerr << "num_modes is invalid: " << num_modes << std::endl; - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - if (num_params != 2 * context->paramNames.size()) - throw std::runtime_error( - fmt::format("[Internal Error] Invalid number of tensor callback " - "parameters. Expected {} double values " - "representing {} complex parameters but received " - "{}.", - std::to_string(2 * context->paramNames.size()), - std::to_string(context->paramNames.size()), - std::to_string(num_params))); - - std::unordered_map> param_map; - for (size_t i = 0; i < context->paramNames.size(); ++i) { - param_map[context->paramNames[i]] = - std::complex(params[2 * i], params[2 * i + 1]); - cudaq::debug("Tensor callback param name {}, value {}", - context->paramNames[i], param_map[context->paramNames[i]]); - } - - std::unordered_map dimensions; - for (int i = 0; i < num_modes; ++i) { - dimensions[i] = static_cast(mode_extents[i]); - } - - if (dimensions.empty()) { - std::cerr << "Dimension map is empty!" << std::endl; - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - matrix_2 matrix_data = stored_op.to_matrix(dimensions, param_map); - - std::size_t rows = matrix_data.get_rows(); - std::size_t cols = matrix_data.get_columns(); - - if (rows != cols) { - std::cerr << "Non-square matrix encountered: " << rows << "x" << cols - << std::endl; - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - std::vector> flat_matrix = - flatten_matrix(matrix_data); - - if (data_type == CUDA_C_64F) { - memcpy(tensor_storage, flat_matrix.data(), - flat_matrix.size() * sizeof(cuDoubleComplex)); - } else if (data_type == CUDA_C_32F) { - std::vector flat_matrix_float(flat_matrix.size()); - for (size_t i = 0; i < flat_matrix.size(); i++) { - flat_matrix_float[i] = - make_cuFloatComplex(static_cast(flat_matrix[i].real()), - static_cast(flat_matrix[i].imag())); - } - memcpy(tensor_storage, flat_matrix_float.data(), - flat_matrix_float.size() * sizeof(cuFloatComplex)); - } else { - std::cerr << "Invalid CUDA data type: " << data_type << std::endl; - return CUDENSITYMAT_STATUS_INVALID_VALUE; - } - - return CUDENSITYMAT_STATUS_SUCCESS; - } catch (const std::exception &e) { - std::cerr << "Error in tensor callback: " << e.what() << std::endl; - return CUDENSITYMAT_STATUS_INTERNAL_ERROR; - } - }; - - cudensitymatWrappedTensorCallback_t wrapped_callback; - wrapped_callback.callback = - reinterpret_cast(stored_callback_context); - wrapped_callback.wrapper = - reinterpret_cast(static_cast(wrapper)); - - return wrapped_callback; -} - -// Function to flatten a matrix into a 1D array (column major) -std::vector> -cudm_helper::flatten_matrix(const matrix_2 &matrix) { - std::vector> flat_matrix; - flat_matrix.reserve(matrix.get_size()); - for (size_t col = 0; col < matrix.get_columns(); col++) { - for (size_t row = 0; row < matrix.get_rows(); row++) { - flat_matrix.push_back(matrix[{row, col}]); - } - } - - return flat_matrix; -} - -// Function to extract sub-space extents based on degrees -std::vector -cudm_helper::get_subspace_extents(const std::vector &mode_extents, - const std::vector °rees) { - std::vector subspace_extents; - - for (int degree : degrees) { - if (degree >= mode_extents.size()) { - throw std::out_of_range("Degree exceeds mode_extents size."); - } - subspace_extents.push_back(mode_extents[degree]); - } - - return subspace_extents; -} - -void cudm_helper::print_complex_vector( - const std::vector> &vec) { - size_t n = static_cast(std::sqrt(vec.size())); - - std::cout << "Vector contents: [\n"; - for (size_t i = 0; i < n; i++) { - std::cout << "["; - for (size_t j = 0; j < n; j++) { - size_t index = i * n + j; - std::cout << " (" << vec[index].real() << ", " << vec[index].imag() - << "i) "; - } - std::cout << "]\n"; - } -} - -// Function to create a cudensitymat elementary operator -// Need to use std::variant -cudensitymatElementaryOperator_t cudm_helper::create_elementary_operator( - const cudaq::matrix_operator &elem_op, - const std::unordered_map> ¶meters, - const std::vector &mode_extents) { - auto subspace_extents = get_subspace_extents(mode_extents, elem_op.degrees()); - std::unordered_map dimensions = convert_dimensions(mode_extents); - auto flat_matrix = flatten_matrix(elem_op.to_matrix(dimensions, parameters)); - - if (flat_matrix.empty()) { - throw std::invalid_argument("Input matrix (flat matrix) cannot be empty."); - } - - if (subspace_extents.empty()) { - throw std::invalid_argument("subspace_extents cannot be empty."); - } - - cudensitymatWrappedTensorCallback_t wrapped_tensor_callback = {nullptr, - nullptr}; - - if (!parameters.empty()) { - const std::map> sortedParameters( - parameters.begin(), parameters.end()); - auto ks = std::views::keys(sortedParameters); - const std::vector keys{ks.begin(), ks.end()}; - - wrapped_tensor_callback = _wrap_tensor_callback(elem_op, keys); - } - - auto *elementaryMat_d = create_array_gpu(flat_matrix); - cudensitymatElementaryOperator_t cudm_elem_op = nullptr; - - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, static_cast(subspace_extents.size()), - subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, - CUDA_C_64F, elementaryMat_d, wrapped_tensor_callback, &cudm_elem_op)); - - if (!cudm_elem_op) { - std::cerr << "[ERROR] cudm_elem_op is NULL in create_elementary_operator!" - << std::endl; - destroy_array_gpu(elementaryMat_d); - throw std::runtime_error("Failed to create elementary operator."); - } - m_elementaryOperators.emplace(cudm_elem_op); - m_deviceBuffers.emplace(elementaryMat_d); - return cudm_elem_op; -} - -// Function to append an elementary operator to a term -void cudm_helper::append_elementary_operator_to_term( - cudensitymatOperatorTerm_t term, - const std::vector &elem_ops, - const std::vector> °rees, - const std::vector> &all_action_dual_modalities) { - - if (degrees.empty()) { - throw std::invalid_argument("Degrees vector cannot be empty."); - } - - if (elem_ops.empty()) { - throw std::invalid_argument("elem_ops cannot be null."); - } - - if (degrees.size() != elem_ops.size()) { - throw std::invalid_argument( - "elem_ops and degrees must have the same size."); - } - - bool has_dual_modalities = !all_action_dual_modalities.empty(); - - if (has_dual_modalities && - degrees.size() != all_action_dual_modalities.size()) { - throw std::invalid_argument( - "degrees and all_action_dual_modalities must have the same size."); - } - - std::vector allDegrees; - std::vector allModeActionDuality; - for (size_t i = 0; i < degrees.size(); i++) { - const auto &sub_degrees = degrees[i]; - const auto &modalities = has_dual_modalities - ? all_action_dual_modalities[i] - : std::vector(sub_degrees.size(), 0); - - if (sub_degrees.size() != modalities.size()) { - throw std::runtime_error( - "Mismatch between degrees and modalities sizes."); - } - if (sub_degrees.size() != 1) { - throw std::runtime_error( - "Elementary operator must act on a single degree."); - } - - for (size_t j = 0; j < sub_degrees.size(); j++) { - int degree = sub_degrees[j]; - int modality = modalities[j]; - - if (sub_degrees[i] < 0) { - throw std::out_of_range("Degree cannot be negative!"); - } - allDegrees.emplace_back(degree); - allModeActionDuality.emplace_back(modality); - } - } - - assert(elem_ops.size() == degrees.size()); - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, term, static_cast(elem_ops.size()), elem_ops.data(), - allDegrees.data(), allModeActionDuality.data(), - make_cuDoubleComplex(1.0, 0.0), {nullptr, nullptr})); -} - -void cudm_helper::scale_state(cudensitymatState_t state, double scale_factor, - cudaStream_t stream) { - if (!state) { - throw std::invalid_argument("Invalid state provided to scale_state."); - } - - HANDLE_CUDM_ERROR( - cudensitymatStateComputeScaling(handle, state, &scale_factor, stream)); - - HANDLE_CUDA_ERROR(cudaStreamSynchronize(stream)); -} - -static product_operator -computeDagger(const cudaq::matrix_operator &op) { - const std::string daggerOpName = op.to_string(false) + "_dagger"; - try { - auto func = [op](const std::vector &dimensions, - const std::unordered_map> - ¶ms) { - std::unordered_map dims; - if (dimensions.size() != op.degrees().size()) - throw std::runtime_error("Dimension mismatched"); - - for (int i = 0; i < dimensions.size(); ++i) { - dims[op.degrees()[i]] = dimensions[i]; - } - auto originalMat = op.to_matrix(dims, params); - return matrix_2::adjoint(originalMat); - }; - matrix_operator::define(daggerOpName, {-1}, std::move(func)); - } catch (...) { - // Nothing, this has been define - } - return matrix_operator::instantiate(daggerOpName, op.degrees()); -} - -static scalar_operator computeDagger(const scalar_operator &scalar) { - if (scalar.is_constant()) { - return scalar_operator(std::conj(scalar.evaluate())); - } else { - return scalar_operator( - [scalar]( - const std::unordered_map> ¶ms) - -> std::complex { - return std::conj(scalar.evaluate(params)); - }); - } -} - -static product_operator -computeDagger(const product_operator &productOp) { - std::vector> daggerOps; - for (const auto &component : productOp.get_terms()) { - if (const auto *elem_op = - dynamic_cast(&component)) { - daggerOps.emplace_back(computeDagger(*elem_op)); - } else { - throw std::runtime_error("Unhandled type!"); - } - } - std::reverse(daggerOps.begin(), daggerOps.end()); - - if (daggerOps.empty()) { - throw std::runtime_error("Empty product operator"); - } - product_operator daggerProduct = daggerOps[0]; - for (std::size_t i = 1; i < daggerOps.size(); ++i) { - daggerProduct *= daggerOps[i]; - } - daggerProduct *= computeDagger(productOp.get_coefficient()); - return daggerProduct; -} - -static operator_sum -computeDagger(const operator_sum &sumOp) { - - const auto &productTerms = sumOp.get_terms(); - - if (productTerms.empty()) { - throw std::runtime_error("Empty operator sum"); - } - product_operator firstDaggerTerm = - computeDagger(productTerms[0]); - operator_sum daggerOpSum(firstDaggerTerm); - - for (std::size_t i = 1; i < productTerms.size(); ++i) { - daggerOpSum += computeDagger(productTerms[i]); - } - - return daggerOpSum; -} - -std::vector> -cudm_helper::compute_lindblad_terms( - operator_sum &collapseOp, - const std::vector &mode_extents, - const std::unordered_map> ¶meters) { - std::vector> - lindbladTerms; - for (const product_operator &l_op : collapseOp.get_terms()) { - for (const product_operator &r_op : - collapseOp.get_terms()) { - scalar_operator coeff = - l_op.get_coefficient() * computeDagger(r_op.get_coefficient()); - auto ldag = computeDagger(r_op); - { - // L * rho * L_dag - std::vector elem_ops; - std::vector> all_degrees; - std::vector> all_action_dual_modalities; - - for (const auto &component : l_op.get_terms()) { - if (const auto *elem_op = - dynamic_cast(&component)) { - auto cudm_elem_op = - create_elementary_operator(*elem_op, parameters, mode_extents); - elem_ops.emplace_back(cudm_elem_op); - all_degrees.emplace_back(elem_op->degrees()); - all_action_dual_modalities.emplace_back( - std::vector(elem_op->degrees().size(), 0)); - } else { - // Catch anything that we don't know - throw std::runtime_error("Unhandled type!"); - } - } - - for (const auto &component : ldag.get_terms()) { - if (const auto *elem_op = - dynamic_cast(&component)) { - auto cudm_elem_op = - create_elementary_operator(*elem_op, parameters, mode_extents); - elem_ops.emplace_back(cudm_elem_op); - all_degrees.emplace_back(elem_op->degrees()); - all_action_dual_modalities.emplace_back( - std::vector(elem_op->degrees().size(), 1)); - } else { - // Catch anything that we don't know - throw std::runtime_error("Unhandled type!"); - } - } - - cudensitymatOperatorTerm_t D1_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D1_term)); // the created empty operator term - m_operatorTerms.emplace(D1_term); - - append_elementary_operator_to_term(D1_term, elem_ops, all_degrees, - all_action_dual_modalities); - lindbladTerms.emplace_back(std::make_pair(coeff, D1_term)); - } - - product_operator L_daggerTimesL = -0.5 * ldag * l_op; - { - std::vector elem_ops; - std::vector> all_degrees; - std::vector> all_action_dual_modalities_left; - std::vector> all_action_dual_modalities_right; - for (const auto &component : L_daggerTimesL.get_terms()) { - if (const auto *elem_op = - dynamic_cast(&component)) { - auto cudm_elem_op = - create_elementary_operator(*elem_op, parameters, mode_extents); - elem_ops.emplace_back(cudm_elem_op); - all_degrees.emplace_back(elem_op->degrees()); - all_action_dual_modalities_left.emplace_back( - std::vector(elem_op->degrees().size(), 0)); - all_action_dual_modalities_right.emplace_back( - std::vector(elem_op->degrees().size(), 1)); - } else { - // Catch anything that we don't know - throw std::runtime_error("Unhandled type!"); - } - } - { - cudensitymatOperatorTerm_t D2_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D2_term)); // the created empty operator term - m_operatorTerms.emplace(D2_term); - // For left side, we need to reverse the order - std::vector d2Ops(elem_ops); - std::reverse(d2Ops.begin(), d2Ops.end()); - std::vector> d2Degrees(all_degrees); - std::reverse(d2Degrees.begin(), d2Degrees.end()); - append_elementary_operator_to_term(D2_term, d2Ops, d2Degrees, - all_action_dual_modalities_left); - lindbladTerms.emplace_back( - std::make_pair(L_daggerTimesL.get_coefficient(), D2_term)); - } - { - cudensitymatOperatorTerm_t D3_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D3_term)); // the created empty operator term - m_operatorTerms.emplace(D3_term); - - append_elementary_operator_to_term(D3_term, elem_ops, all_degrees, - all_action_dual_modalities_right); - lindbladTerms.emplace_back( - std::make_pair(L_daggerTimesL.get_coefficient(), D3_term)); - } - } - } - } - return lindbladTerms; -} - -std::pair -cudm_helper::compute_lindblad_operator_terms( - operator_sum &collapseOp, - const std::vector &mode_extents, - const std::unordered_map> ¶meters) { - std::unordered_map dimensions; - for (int i = 0; i < mode_extents.size(); ++i) - dimensions[i] = mode_extents[i]; - auto c_op = collapseOp.to_matrix(dimensions); - auto degrees = collapseOp.degrees(); - auto adjointMat = matrix_2::adjoint(c_op); - cudensitymatElementaryOperator_t LOp, LOpDagger, LdaggerLOp; - auto *LOp_d = create_array_gpu(flatten_matrix(c_op)); - auto *LOpDagger_d = create_array_gpu(flatten_matrix(adjointMat)); - auto *LdaggerL_d = create_array_gpu(flatten_matrix(adjointMat * c_op)); - m_deviceBuffers.emplace(LOp_d); - m_deviceBuffers.emplace(LOpDagger_d); - m_deviceBuffers.emplace(LdaggerL_d); - auto subspace_extents = get_subspace_extents(mode_extents, degrees); - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, subspace_extents.size(), subspace_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, LOp_d, - {nullptr, nullptr}, &LOp)); - - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, subspace_extents.size(), subspace_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, LOpDagger_d, - {nullptr, nullptr}, &LOpDagger)); - - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, subspace_extents.size(), subspace_extents.data(), - CUDENSITYMAT_OPERATOR_SPARSITY_NONE, 0, nullptr, CUDA_C_64F, LdaggerL_d, - {nullptr, nullptr}, &LdaggerLOp)); - m_elementaryOperators.emplace(LOp); - m_elementaryOperators.emplace(LOpDagger); - m_elementaryOperators.emplace(LdaggerLOp); - - cudensitymatOperatorTerm_t D1_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D1_term)); // the created empty operator term - m_operatorTerms.emplace(D1_term); - // Define the operator term - std::vector d1Degree; // stacked degrees - d1Degree.insert(d1Degree.end(), degrees.begin(), degrees.end()); - d1Degree.insert(d1Degree.end(), degrees.begin(), degrees.end()); - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, D1_term, - 2, // number of elementary tensor operators in the product - std::vector({LOp, LOpDagger}) - .data(), // elementary tensor operators forming the product - d1Degree.data(), // space modes acted on by the operator product (from - // different sides) - std::vector({0, 1}).data(), // space mode action duality (0: from - // the left; 1: from the right) - make_cuDoubleComplex(1.0, 0.0), // default coefficient: Always - // 64-bit-precision complex number - {nullptr, nullptr})); // no time-dependent coefficient associated with - // the operator product - - cudensitymatOperatorTerm_t D2_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D2_term)); // the created empty operator term - m_operatorTerms.emplace(D2_term); - // Define the operator term - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, D2_term, - 1, // number of elementary tensor operators in the product - std::vector({LdaggerLOp}) - .data(), // elementary tensor operators forming the product - degrees.data(), // space modes acted on by the operator - // product (from different sides) - std::vector({0}).data(), // space mode action duality (0: - // from the left; 1: from the right) - make_cuDoubleComplex(-0.5, 0.0), // default coefficient: Always - // 64-bit-precision complex number - {nullptr, nullptr})); // no time-dependent coefficient associated with - // the operator product - - return std::make_pair(D1_term, D2_term); -} - -// TODO: fix the signature -// c_ops: std::vector -cudensitymatOperator_t cudm_helper::compute_lindblad_operator( - const std::vector &c_ops, - const std::vector &mode_extents) { - if (c_ops.empty()) { - throw std::invalid_argument("Collapse operators cannot be empty."); - } - - cudensitymatOperator_t liouvillian; - - // Create an empty operator (super-operator) - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &liouvillian)); // the created empty operator (super-operator) - - for (auto &c_op : c_ops) { - - cudensitymatElementaryOperator_t LOp, LOpDagger, LdaggerLOp; - auto adjointMat = matrix_2::adjoint(c_op); - auto *LOp_d = create_array_gpu(flatten_matrix(c_op)); - auto *LOpDagger_d = create_array_gpu(flatten_matrix(adjointMat)); - auto *LdaggerL_d = create_array_gpu(flatten_matrix(adjointMat * c_op)); - m_deviceBuffers.emplace(LOp_d); - m_deviceBuffers.emplace(LOpDagger_d); - m_deviceBuffers.emplace(LdaggerL_d); - // FIXME: assume degree 0 as we don't have that info here - auto subspace_extents = get_subspace_extents(mode_extents, {0}); - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, 1, subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, - 0, nullptr, CUDA_C_64F, LOp_d, {nullptr, nullptr}, &LOp)); - - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, 1, subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, - 0, nullptr, CUDA_C_64F, LOpDagger_d, {nullptr, nullptr}, &LOpDagger)); - - HANDLE_CUDM_ERROR(cudensitymatCreateElementaryOperator( - handle, 1, subspace_extents.data(), CUDENSITYMAT_OPERATOR_SPARSITY_NONE, - 0, nullptr, CUDA_C_64F, LdaggerL_d, {nullptr, nullptr}, &LdaggerLOp)); - m_elementaryOperators.emplace(LOp); - m_elementaryOperators.emplace(LOpDagger); - m_elementaryOperators.emplace(LdaggerLOp); - { - cudensitymatOperatorTerm_t D1_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D1_term)); // the created empty operator term - m_operatorTerms.emplace(D1_term); - // Define the operator term - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, D1_term, - 2, // number of elementary tensor operators in the product - std::vector({LOp, LOpDagger}) - .data(), // elementary tensor operators forming the product - std::vector({0, 0}) - .data(), // space modes acted on by the operator product (from - // different sides) - std::vector({0, 1}) - .data(), // space mode action duality (0: from - // the left; 1: from the right) - make_cuDoubleComplex(1.0, 0.0), // default coefficient: Always - // 64-bit-precision complex number - {nullptr, nullptr})); // no time-dependent coefficient associated with - // the operator product - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, - D1_term, // appended operator term - 0, // operator term action duality as a whole (no duality reversing in - // this case) - make_cuDoubleComplex(1, 0.0), // constant coefficient associated with - // the operator term as a whole - {nullptr, - nullptr})); // no time-dependent coefficient associated with the - } - { - cudensitymatOperatorTerm_t D2_term; - // Create an empty operator term - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, - mode_extents.size(), // Hilbert space rank (number of dimensions) - mode_extents.data(), // Hilbert space shape - &D2_term)); // the created empty operator term - m_operatorTerms.emplace(D2_term); - // Define the operator term - HANDLE_CUDM_ERROR(cudensitymatOperatorTermAppendElementaryProduct( - handle, D2_term, - 1, // number of elementary tensor operators in the product - std::vector({LdaggerLOp}) - .data(), // elementary tensor operators forming the product - std::vector({0}) - .data(), // space modes acted on by the operator product (from - // different sides) - std::vector({0}).data(), // space mode action duality (0: - // from the left; 1: from the right) - make_cuDoubleComplex(-0.5, 0.0), // default coefficient: Always - // 64-bit-precision complex number - {nullptr, nullptr})); // no time-dependent coefficient associated with - // the operator product - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, - D2_term, // appended operator term - 0, // operator term action duality as a whole (no duality reversing in - // this case) - make_cuDoubleComplex(1, 0.0), // constant coefficient associated with - // the operator term as a whole - {nullptr, - nullptr})); // no time-dependent coefficient associated with the - - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, - D2_term, // appended operator term - 1, // operator term action duality as a whole (no duality reversing in - // this case) - make_cuDoubleComplex(1, 0.0), // constant coefficient associated with - // the operator term as a whole - {nullptr, - nullptr})); // no time-dependent coefficient associated with the - } - } - // operator term as a whole - std::cout << "Constructed the Liouvillian operator\n"; - return liouvillian; -} - -std::unordered_map -cudm_helper::convert_dimensions(const std::vector &mode_extents) { - - std::unordered_map dimensions; - for (size_t i = 0; i < mode_extents.size(); i++) { - dimensions[static_cast(i)] = static_cast(mode_extents[i]); - } - - return dimensions; -} - -std::vector> -cudm_helper::convert_to_cudensitymat( - const operator_sum &op, - const std::unordered_map> ¶meters, - const std::vector &mode_extents) { - if (op.get_terms().empty()) { - throw std::invalid_argument("Operator sum cannot be empty."); - } - - std::vector> - result; - - for (const auto &product_op : op.get_terms()) { - cudensitymatOperatorTerm_t term; - HANDLE_CUDM_ERROR(cudensitymatCreateOperatorTerm( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &term)); - m_operatorTerms.emplace(term); - std::vector elem_ops; - std::vector> all_degrees; - for (const auto &component : product_op.get_terms()) { - // No need to check type - // just call to_matrix on it - if (const auto *elem_op = - dynamic_cast(&component)) { - auto cudm_elem_op = - create_elementary_operator(*elem_op, parameters, mode_extents); - elem_ops.emplace_back(cudm_elem_op); - all_degrees.emplace_back(elem_op->degrees()); - } else { - // Catch anything that we don't know - throw std::runtime_error("Unhandled type!"); - } - } - append_elementary_operator_to_term(term, elem_ops, all_degrees, {}); - result.emplace_back(std::make_pair(product_op.get_coefficient(), term)); - } - return result; -} - -template -cudensitymatOperator_t cudm_helper::convert_to_cudensitymat_operator( - const std::unordered_map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents) { - if (op.get_terms().empty()) { - throw std::invalid_argument("Operator sum cannot be empty."); - } - - cudensitymatOperator_t operator_handle; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &operator_handle)); - - const std::map> sortedParameters( - parameters.begin(), parameters.end()); - auto ks = std::views::keys(sortedParameters); - const std::vector keys{ks.begin(), ks.end()}; - for (auto &[coeff, term] : - convert_to_cudensitymat(op, parameters, mode_extents)) { - cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - - if (coeff.is_constant()) { - const auto coeffVal = coeff.evaluate(); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, - make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), - wrapped_callback)); - } else { - wrapped_callback = _wrap_callback(coeff, keys); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, operator_handle, term, 0, make_cuDoubleComplex(1.0, 0.0), - wrapped_callback)); - } - } - - return operator_handle; -} - -cudensitymatOperator_t cudm_helper::construct_liouvillian( - const operator_sum &op, - const std::vector *> - &collapse_operators, - const std::vector &mode_extents, - const std::unordered_map> ¶meters, - bool is_master_equation) { - if (!is_master_equation && collapse_operators.empty()) { - cudaq::info("Construct state vector Liouvillian"); - auto liouvillian = op * std::complex(0.0, -1.0); - return convert_to_cudensitymat_operator(parameters, liouvillian, - mode_extents); - } else { - cudaq::info("Construct density matrix Liouvillian"); - cudensitymatOperator_t liouvillian; - HANDLE_CUDM_ERROR(cudensitymatCreateOperator( - handle, static_cast(mode_extents.size()), mode_extents.data(), - &liouvillian)); - // Append an operator term to the operator (super-operator) - // Handle the Hamiltonian - const std::map> sortedParameters( - parameters.begin(), parameters.end()); - auto ks = std::views::keys(sortedParameters); - const std::vector keys{ks.begin(), ks.end()}; - for (auto &[coeff, term] : - convert_to_cudensitymat(op, parameters, mode_extents)) { - cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, nullptr}; - if (coeff.is_constant()) { - const auto coeffVal = coeff.evaluate(); - const auto leftCoeff = std::complex(0.0, -1.0) * coeffVal; - // -i constant (left multiplication) - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, term, 0, - make_cuDoubleComplex(leftCoeff.real(), leftCoeff.imag()), - wrapped_callback)); - - // +i constant (right multiplication, i.e., dual) - const auto rightCoeff = std::complex(0.0, 1.0) * coeffVal; - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, term, 1, - make_cuDoubleComplex(rightCoeff.real(), rightCoeff.imag()), - wrapped_callback)); - } else { - wrapped_callback = _wrap_callback(coeff, keys); - // -i constant (left multiplication) - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, term, 0, make_cuDoubleComplex(0.0, -1.0), - wrapped_callback)); - - // +i constant (right multiplication, i.e., dual) - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, term, 1, make_cuDoubleComplex(0.0, 1.0), - wrapped_callback)); - } - } - - // Handle collapsed operators - for (auto &collapse_operators : collapse_operators) { - for (auto &[coeff, term] : compute_lindblad_terms( - *collapse_operators, mode_extents, parameters)) { - cudensitymatWrappedScalarCallback_t wrapped_callback = {nullptr, - nullptr}; - if (coeff.is_constant()) { - const auto coeffVal = coeff.evaluate(); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, term, 0, - make_cuDoubleComplex(coeffVal.real(), coeffVal.imag()), - wrapped_callback)); - } else { - wrapped_callback = _wrap_callback(coeff, keys); - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, term, 0, make_cuDoubleComplex(1.0, 0.0), - wrapped_callback)); - } - } - } - - return liouvillian; - } -} - -cudensitymatOperator_t cudm_helper::construct_liouvillian( - const cudensitymatOperator_t &hamiltonian, - const std::vector &collapse_operators, - double gamma) { - try { - cudensitymatOperator_t liouvillian; - HANDLE_CUDM_ERROR( - cudensitymatCreateOperator(handle, 0, nullptr, &liouvillian)); - - cudensitymatWrappedScalarCallback_t scalarCallback = {nullptr, nullptr}; - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, hamiltonian, 0, {1.0, 0.0}, scalarCallback)); - - // Collapse operator scaled by gamma - cuDoubleComplex coefficient = make_cuDoubleComplex(gamma, 0.0); - for (const auto &c_op : collapse_operators) { - HANDLE_CUDM_ERROR(cudensitymatOperatorAppendTerm( - handle, liouvillian, c_op, 0, coefficient, scalarCallback)); - } - - return liouvillian; - } catch (const std::exception &e) { - std::cerr << "Error in construct_liouvillian: " << e.what() << std::endl; - throw; - } -} - -// Function for creating an array copy in GPU memory -void *cudm_helper::create_array_gpu( - const std::vector> &cpu_array) { - void *gpu_array{nullptr}; - const std::size_t array_size = - cpu_array.size() * sizeof(std::complex); - if (array_size > 0) { - HANDLE_CUDA_ERROR(cudaMalloc(&gpu_array, array_size)); - HANDLE_CUDA_ERROR(cudaMemcpy(gpu_array, - static_cast(cpu_array.data()), - array_size, cudaMemcpyHostToDevice)); - } - return gpu_array; -} - -// Function to detsroy a previously created array copy in GPU memory -void cudm_helper::destroy_array_gpu(void *gpu_array) { - if (gpu_array) { - HANDLE_CUDA_ERROR(cudaFree(gpu_array)); - } -} - -template cudensitymatOperator_t -cudm_helper::convert_to_cudensitymat_operator( - const std::unordered_map> &, - const operator_sum &, const std::vector &); - -} // namespace cudaq diff --git a/runtime/nvqir/cudensitymat/cudm_helpers.h b/runtime/nvqir/cudensitymat/cudm_helpers.h deleted file mode 100644 index f109a703c9..0000000000 --- a/runtime/nvqir/cudensitymat/cudm_helpers.h +++ /dev/null @@ -1,121 +0,0 @@ -/****************************************************************-*- C++ -*-**** - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#pragma once - -#include "cudaq/operators.h" -#include "cudaq/utils/tensor.h" -#include -#include -#include -#include -#include -#include -namespace cudaq { -class cudm_helper { -public: - explicit cudm_helper(cudensitymatHandle_t handle); - ~cudm_helper(); - - // Matrix flattening - static std::vector> - flatten_matrix(const matrix_2 &matrix); - - static void - print_complex_vector(const std::vector> &vec); - - // State Operations - void scale_state(cudensitymatState_t state, double scale_factor, - cudaStream_t stream); - - // Compute Lindblad Operator - cudensitymatOperator_t - compute_lindblad_operator(const std::vector &c_ops, - const std::vector &mode_extents); - - // Convert operator sum to cudensitymat operator - template - cudensitymatOperator_t convert_to_cudensitymat_operator( - const std::unordered_map> ¶meters, - const operator_sum &op, - const std::vector &mode_extents); - std::vector> - compute_lindblad_terms( - operator_sum &collapseOp, - const std::vector &mode_extents, - const std::unordered_map> ¶meters); - std::vector> - convert_to_cudensitymat( - const operator_sum &op, - const std::unordered_map> ¶meters, - const std::vector &mode_extents); - // Construct Liouvillian - cudensitymatOperator_t construct_liouvillian( - const operator_sum &op, - const std::vector *> - &collapse_operators, - const std::vector &mode_extents, - const std::unordered_map> ¶meters, - bool is_master_equation); - - // Construct Liouvillian - cudensitymatOperator_t construct_liouvillian( - const cudensitymatOperator_t &hamiltonian, - const std::vector &collapse_operators, - double gamma); - - std::pair - compute_lindblad_operator_terms( - operator_sum &collapseOp, - const std::vector &mode_extents, - const std::unordered_map> ¶meters); - - // Helper Functions - std::unordered_map - convert_dimensions(const std::vector &mode_extents); - std::vector - get_subspace_extents(const std::vector &mode_extents, - const std::vector °rees); - - // Callback Wrappers - static cudensitymatWrappedScalarCallback_t - _wrap_callback(const scalar_operator &scalar_op, - const std::vector ¶mNames); - static cudensitymatWrappedTensorCallback_t - _wrap_tensor_callback(const matrix_operator &op, - const std::vector ¶mNames); - - cudensitymatElementaryOperator_t create_elementary_operator( - const cudaq::matrix_operator &elem_op, - const std::unordered_map> ¶meters, - const std::vector &mode_extents); - void append_elementary_operator_to_term( - cudensitymatOperatorTerm_t term, - const std::vector &elem_ops, - const std::vector> °rees, - const std::vector> &all_action_dual_modalities); - - // GPU memory management - static void * - create_array_gpu(const std::vector> &cpu_array); - static void destroy_array_gpu(void *gpu_array); - -private: - cudensitymatHandle_t handle; - // Things that we create that need to be cleaned up. - // Use a set so that it's safe to push pointer multiple times. - std::unordered_set m_deviceBuffers; - std::unordered_set m_elementaryOperators; - std::unordered_set m_operatorTerms; -}; - -extern template cudensitymatOperator_t -cudm_helper::convert_to_cudensitymat_operator( - const std::unordered_map> &, - const operator_sum &, const std::vector &); -} // namespace cudaq \ No newline at end of file diff --git a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp index 5fde9d979b..0bc4fd10d5 100644 --- a/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp +++ b/runtime/nvqir/cudensitymat/cudm_time_stepper.cpp @@ -9,7 +9,6 @@ #include "cudm_time_stepper.h" #include "CuDensityMatContext.h" #include "cudm_error_handling.h" -#include "cudm_helpers.h" namespace cudaq { cudmStepper::cudmStepper(cudensitymatHandle_t handle, cudensitymatOperator_t liouvillian) diff --git a/unittests/CMakeLists.txt b/unittests/CMakeLists.txt index 8c153e1c38..0b4c943c53 100644 --- a/unittests/CMakeLists.txt +++ b/unittests/CMakeLists.txt @@ -293,7 +293,6 @@ if (CUDA_FOUND) set(CUDAQ_DYNAMICS_TEST_SOURCES dynamics/test_runge_kutta_integrator.cpp dynamics/test_helpers.cpp - dynamics/test_cudm_helpers.cpp dynamics/test_cudm_state.cpp dynamics/test_cudm_time_stepper.cpp dynamics/test_cudm_expectation.cpp diff --git a/unittests/dynamics/test_cudm_expectation.cpp b/unittests/dynamics/test_cudm_expectation.cpp index 30f77b14a2..1a9a012d12 100644 --- a/unittests/dynamics/test_cudm_expectation.cpp +++ b/unittests/dynamics/test_cudm_expectation.cpp @@ -6,12 +6,12 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatContext.h" #include "CuDensityMatState.h" #include "common/EigenDense.h" #include "test_mocks.h" #include #include -#include #include #include #include @@ -35,12 +35,12 @@ class CuDensityExpectationTest : public ::testing::Test { }; TEST_F(CuDensityExpectationTest, checkCompute) { - cudm_helper helper(handle_); const std::vector dims = {10}; // Check number operator on boson Fock space auto op = cudaq::matrix_operator::number(0); - auto cudmOp = helper.convert_to_cudensitymat_operator( - {}, op, dims); + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator({}, op, dims); cudm_expectation expectation(handle_, cudmOp); @@ -57,12 +57,12 @@ TEST_F(CuDensityExpectationTest, checkCompute) { } TEST_F(CuDensityExpectationTest, checkCompositeSystem) { - cudm_helper helper(handle_); const std::vector dims = {2, 10}; // Check number operator on boson Fock space auto op = cudaq::matrix_operator::number(1); - auto cudmOp = helper.convert_to_cudensitymat_operator( - {}, op, dims); + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator({}, op, dims); cudm_expectation expectation(handle_, cudmOp); diff --git a/unittests/dynamics/test_cudm_helpers.cpp b/unittests/dynamics/test_cudm_helpers.cpp deleted file mode 100644 index 74e94b64d9..0000000000 --- a/unittests/dynamics/test_cudm_helpers.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2022 - 2025 NVIDIA Corporation & Affiliates. * - * All rights reserved. * - * * - * This source code and the accompanying materials are made available under * - * the terms of the Apache License 2.0 which accompanies this distribution. * - ******************************************************************************/ - -#include "CuDensityMatState.h" -#include "test_mocks.h" -#include -#include -#include -#include - -// Initialize operator_sum -cudaq::operator_sum initialize_operator_sum() { - return cudaq::boson_operator::create(0) + cudaq::boson_operator::create(1); -} - -class CuDensityMatHelpersTestFixture : public ::testing::Test { -protected: - cudensitymatHandle_t handle; - cudaStream_t stream; - std::unique_ptr helper; - std::unique_ptr state; - - void SetUp() override { - HANDLE_CUDM_ERROR(cudensitymatCreate(&handle)); - stream = 0; - helper = std::make_unique(handle); - - std::vector mode_extents = {2}; - std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - state = std::make_unique(handle, rawData, - mode_extents); - } - - void TearDown() override { HANDLE_CUDA_ERROR(cudaDeviceSynchronize()); } -}; - -// Test for initialize_state -TEST_F(CuDensityMatHelpersTestFixture, InitializeState) { - std::vector mode_extents = {2}; - - std::vector> rawData = {{1.0, 0.0}, {0.0, 0.0}}; - - cudaq::CuDensityMatState state(handle, rawData, mode_extents); - - ASSERT_TRUE(state.is_initialized()); -} - -// Test for scale_state -// TEST_F(CuDensityMatHelpersTestFixture, ScaleState) { -// ASSERT_TRUE(state->is_initialized()); - -// EXPECT_NO_THROW(helper->scale_state(state->get_impl(), 2.0, -// stream)); -// } - -// Test for compute_lindblad_op -TEST_F(CuDensityMatHelpersTestFixture, ComputeLindbladOp) { - std::vector mode_extents = {2, 2}; - - std::vector> c_op1_values = { - {1.0, 0.0}, - {0.0, 0.0}, - {0.0, 0.0}, - {0.0, 0.0}, - }; - - std::vector> c_op2_values = { - {0.0, 0.0}, - {0.0, 1.0}, - {0.0, 0.0}, - {0.0, 0.0}, - }; - - cudaq::matrix_2 c_op1(c_op1_values, {2, 2}); - cudaq::matrix_2 c_op2(c_op2_values, {2, 2}); - std::vector c_ops = {c_op1, c_op2}; - - EXPECT_NO_THROW({ - auto lindblad_op = helper->compute_lindblad_operator(c_ops, mode_extents); - - ASSERT_NE(lindblad_op, nullptr) - << "Error: Lindblad operator creation failed!"; - - cudensitymatDestroyOperator(lindblad_op); - }); -} - -// Test for convert_to_cudensitymat_operator -TEST_F(CuDensityMatHelpersTestFixture, ConvertToCuDensityMatOperator) { - std::vector mode_extents = mock_hilbert_space_dims(); - - auto op_sum = initialize_operator_sum(); - - EXPECT_NO_THROW({ - auto result = - helper->convert_to_cudensitymat_operator( - {}, op_sum, mode_extents); - - ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); - }); -} - -// Test with a higher-dimensional mode extent -TEST_F(CuDensityMatHelpersTestFixture, ConvertHigherDimensionalOperator) { - std::vector mode_extents = {3, 3}; - - auto op_sum = initialize_operator_sum(); - - EXPECT_NO_THROW({ - auto result = - helper->convert_to_cudensitymat_operator( - {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); - }); -} - -// Test with a coefficient callback function -TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithCallback) { - std::vector mode_extents = {2, 2}; - - auto callback_function = - [](std::unordered_map>) { - return std::complex(1.5, 0.0); - }; - - cudaq::ScalarCallbackFunction scalar_callback_function(callback_function); - cudaq::scalar_operator scalar_callback(scalar_callback_function); - - cudaq::product_operator op_sum_1 = - scalar_callback * cudaq::boson_operator::create(0); - cudaq::operator_sum op_sum(op_sum_1); - - EXPECT_NO_THROW({ - auto result = - helper->convert_to_cudensitymat_operator( - {}, op_sum, mode_extents); - ASSERT_NE(result, nullptr); - cudensitymatDestroyOperator(result); - }); -} - -// Test with tensor callback function -TEST_F(CuDensityMatHelpersTestFixture, ConvertOperatorWithTensorCallback) { - std::vector mode_extents = {2, 2}; - - const std::string op_id = "custom_op"; - auto func = [](std::vector dimensions, - std::unordered_map> _none) { - if (dimensions.size() != 1) - throw std::invalid_argument("Must have a singe dimension"); - if (dimensions[0] != 2) - throw std::invalid_argument("Must have dimension 2"); - auto mat = cudaq::matrix_2(2, 2); - mat[{1, 0}] = 1.0; - mat[{0, 1}] = 1.0; - return mat; - }; - cudaq::matrix_operator::define(op_id, {-1}, func); - auto matrix_op = - cudaq::matrix_operator::instantiate(op_id, {0}).get_terms()[0]; - - auto wrapped_tensor_callback = - cudaq::cudm_helper::_wrap_tensor_callback(matrix_op, {}); - - ASSERT_NE(wrapped_tensor_callback.callback, nullptr); - - // auto op_sum = matrix_op + matrix_op; - - // EXPECT_NO_THROW({ - // auto result = - // helper->convert_to_cudensitymat_operator( - // {}, op_sum, mode_extents); - // ASSERT_NE(result, nullptr); - // cudensitymatDestroyOperator(result); - // }); -} diff --git a/unittests/dynamics/test_cudm_state.cpp b/unittests/dynamics/test_cudm_state.cpp index 7e554864cd..88a9762a4e 100644 --- a/unittests/dynamics/test_cudm_state.cpp +++ b/unittests/dynamics/test_cudm_state.cpp @@ -9,7 +9,6 @@ #include "CuDensityMatState.h" #include #include -#include #include #include #include diff --git a/unittests/dynamics/test_cudm_time_stepper.cpp b/unittests/dynamics/test_cudm_time_stepper.cpp index 2ff8a53c1a..efa34f567e 100644 --- a/unittests/dynamics/test_cudm_time_stepper.cpp +++ b/unittests/dynamics/test_cudm_time_stepper.cpp @@ -6,15 +6,14 @@ * the terms of the Apache License 2.0 which accompanies this distribution. * ******************************************************************************/ +#include "CuDensityMatContext.h" #include "CuDensityMatState.h" #include "cudm_time_stepper.h" #include "test_mocks.h" #include -#include #include #include #include - using namespace cudaq; class CuDensityMatTimeStepperTest : public ::testing::Test { @@ -23,15 +22,11 @@ class CuDensityMatTimeStepperTest : public ::testing::Test { cudensitymatOperator_t liouvillian_; std::unique_ptr time_stepper_; cudaq::state state_ = cudaq::state(nullptr); - std::unique_ptr helper_; void SetUp() override { // Create library handle HANDLE_CUDM_ERROR(cudensitymatCreate(&handle_)); - // Create helper - helper_ = std::make_unique(handle_); - // Create a mock Liouvillian liouvillian_ = mock_liouvillian(handle_); @@ -115,9 +110,10 @@ TEST_F(CuDensityMatTimeStepperTest, ComputeStepCheckOutput) { cudaq::product_operator op_1 = cudaq::boson_operator::create(0); cudaq::operator_sum op(op_1); - auto cudmOp = - helper_->convert_to_cudensitymat_operator( - {}, op, dims); // Initialize the time stepper + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator( + {}, op, dims); // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); auto outputState = time_stepper->compute(inputState, 0.0, 1.0, {}); @@ -149,16 +145,15 @@ TEST_F(CuDensityMatTimeStepperTest, TimeSteppingWithLindblad) { cudaq::operator_sum c_op(c_op_0); cudaq::operator_sum zero_op = 0.0 * c_op; auto cudm_lindblad_op = - helper_->construct_liouvillian(zero_op, {&c_op}, dims, {}, true); + cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .constructLiouvillian(zero_op, {c_op}, dims, {}, true); auto time_stepper = std::make_unique(handle_, cudm_lindblad_op); auto output_state = time_stepper->compute(input_state, 0.0, 1.0, {}); std::vector> output_state_vec(100); output_state.to_host(output_state_vec.data(), output_state_vec.size()); - - helper_->print_complex_vector(output_state_vec); - EXPECT_NEAR( std::abs(output_state_vec[4 * 10 + 4] - std::complex(5.0, 0.0)), 0.0, 1e-12); @@ -196,9 +191,9 @@ TEST_F(CuDensityMatTimeStepperTest, CheckScalarCallback) { cudaq::product_operator op_t = cudaq::scalar_operator(function) * cudaq::boson_operator::create(0); cudaq::operator_sum op(op_t); - auto cudmOp = - helper_->convert_to_cudensitymat_operator( - params, op, dims); + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator(params, op, dims); // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); auto outputState = time_stepper->compute(inputState, 1.0, 1.0, params); @@ -254,9 +249,9 @@ TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { matrix_operator::define("CustomTensorOp", {2}, tensorFunction); auto op = cudaq::matrix_operator::instantiate("CustomTensorOp", {0}); - auto cudmOp = - helper_->convert_to_cudensitymat_operator( - params, op, dims); + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator(params, op, dims); // Initialize the time stepper auto time_stepper = std::make_unique(handle_, cudmOp); auto outputState = time_stepper->compute(inputState, 1.0, 1.0, params); @@ -272,3 +267,82 @@ TEST_F(CuDensityMatTimeStepperTest, CheckTensorCallback) { } HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); } + +TEST_F(CuDensityMatTimeStepperTest, ComputeOperatorOrder) { + const std::vector> initialState = { + {1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}, {1.0, 0.0}}; + const std::vector dims = {4}; + auto inputState = cudaq::state::from_data(initialState); + auto *simState = cudaq::state_helper::getSimulationState(&inputState); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); + + cudaq::product_operator op_t = + cudaq::boson_operator::create(0) * + cudaq::boson_operator::annihilate(0); // a_dagger * a + cudaq::operator_sum op(op_t); + const auto opMat = op.to_matrix({{0, 4}}); + + std::cout << "Op matrix:\n" << opMat.dump() << "\n"; + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .convertToCudensitymatOperator( + {}, op, dims); // Initialize the time stepper + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(inputState, 0.0, 1.0, {}); + std::vector> expectedOutputStateVec(4); + // Diagonal elements + for (std::size_t i = 0; i < expectedOutputStateVec.size(); ++i) + expectedOutputStateVec[i] = opMat[{i, i}]; + + std::vector> outputStateVec(4); + outputState.to_host(outputStateVec.data(), outputStateVec.size()); + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); + for (std::size_t i = 0; i < expectedOutputStateVec.size(); ++i) { + std::cout << "Result = " << outputStateVec[i] + << "; vs. expected = " << expectedOutputStateVec[i] << "\n"; + EXPECT_TRUE(std::abs(expectedOutputStateVec[i] - outputStateVec[i]) < + 1e-12); + } +} + +TEST_F(CuDensityMatTimeStepperTest, ComputeOperatorOrderDensityMatrix) { + constexpr int N = 4; + const std::vector> initialState(N * N, 1.0); + const std::vector dims = {N}; + auto inputState = cudaq::state::from_data(initialState); + auto *simState = cudaq::state_helper::getSimulationState(&inputState); + auto *castSimState = dynamic_cast(simState); + EXPECT_TRUE(castSimState != nullptr); + castSimState->initialize_cudm(handle_, dims); + + cudaq::product_operator op_t = + cudaq::boson_operator::create(0) * + cudaq::boson_operator::annihilate(0); // a_dagger * a + cudaq::operator_sum op(op_t); + const auto opMat = op.to_matrix({{0, N}}); + cudaq::matrix_2 rho = cudaq::matrix_2::identity(N); + for (std::size_t col = 0; col < N; ++col) + for (std::size_t row = 0; row < N; ++row) + rho[{row, col}] = 1.0; + const auto expectedResult = + std::complex(0.0, -1.0) * (opMat * rho - rho * opMat); + std::cout << "Expected result:\n" << expectedResult.dump() << "\n"; + auto cudmOp = cudaq::dynamics::Context::getCurrentContext() + ->getOpConverter() + .constructLiouvillian(op, {}, dims, {}, true); + auto time_stepper = std::make_unique(handle_, cudmOp); + auto outputState = time_stepper->compute(inputState, 0.0, 1.0, {}); + std::vector> outputStateVec(initialState.size()); + outputState.to_host(outputStateVec.data(), outputStateVec.size()); + HANDLE_CUDM_ERROR(cudensitymatDestroyOperator(cudmOp)); + for (std::size_t i = 0; i < outputStateVec.size(); ++i) { + const auto col = i / N; + const auto row = i % N; + std::cout << "Result = " << outputStateVec[i] + << "; vs. expected = " << expectedResult[{row, col}] << "\n"; + EXPECT_TRUE(std::abs(outputStateVec[i] - expectedResult[{row, col}]) < + 1e-12); + } +} diff --git a/unittests/dynamics/test_runge_kutta_integrator.cpp b/unittests/dynamics/test_runge_kutta_integrator.cpp index 4165000c9f..c2d12f7b83 100644 --- a/unittests/dynamics/test_runge_kutta_integrator.cpp +++ b/unittests/dynamics/test_runge_kutta_integrator.cpp @@ -8,7 +8,6 @@ #include "CuDensityMatState.h" #include "cudaq/dynamics_integrators.h" -#include "cudm_helpers.h" #include "cudm_time_stepper.h" #include "test_mocks.h" #include @@ -57,7 +56,6 @@ TEST_F(RungeKuttaIntegratorTest, Initialization) { } TEST_F(RungeKuttaIntegratorTest, CheckEvolve) { - cudm_helper helper(handle_); const std::vector> initialStateVec = {{1.0, 0.0}, {0.0, 0.0}}; const std::vector dims = {2}; From 41dee8433fd07929e4e6a524a73a8c910c7b90e6 Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 21 Feb 2025 00:08:41 +0000 Subject: [PATCH 310/311] Dynamics target uses library mode Signed-off-by: Thien Nguyen --- python/cudaq/operator/dynamics.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/cudaq/operator/dynamics.yml b/python/cudaq/operator/dynamics.yml index 74276909ec..e18a127ddc 100644 --- a/python/cudaq/operator/dynamics.yml +++ b/python/cudaq/operator/dynamics.yml @@ -12,4 +12,4 @@ gpu-requirements: true config: nvqir-simulation-backend: dynamics preprocessor-defines: ["-D CUDAQ_DYNAMICS_TARGET"] - + library-mode: true From 83f96c88629f1af36ff60f9413879f85e380d1de Mon Sep 17 00:00:00 2001 From: Thien Nguyen Date: Fri, 21 Feb 2025 00:45:24 +0000 Subject: [PATCH 311/311] Optimization for skipping tensor callback generation for known ops Signed-off-by: Thien Nguyen --- .../cudensitymat/CuDensityMatOpConverter.cpp | 34 ++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp index 2be84f6e31..f721a72f54 100644 --- a/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp +++ b/runtime/nvqir/cudensitymat/CuDensityMatOpConverter.cpp @@ -228,8 +228,40 @@ cudaq::dynamics::OpConverter::createElementaryOperator( std::unordered_map dimensions = convertDimensions(modeExtents); cudensitymatWrappedTensorCallback_t wrappedTensorCallback = {nullptr, nullptr}; + + static const std::vector g_knownNonParametricOps = []() { + std::vector opNames; + opNames.emplace_back( + cudaq::boson_operator::identity(0).get_terms()[0].to_string(false)); + // These are ops that we created during lindblad generation + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::boson_operator::create(0).get_terms()[0].to_string(false)); + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::boson_operator::annihilate(0).get_terms()[0].to_string(false)); + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::boson_operator::number(0).get_terms()[0].to_string(false)); + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::spin_operator::i(0).get_terms()[0].to_string(false)); + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::spin_operator::x(0).get_terms()[0].to_string(false)); + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::spin_operator::y(0).get_terms()[0].to_string(false)); + opNames.emplace_back(opNames.back() + "_dagger"); + opNames.emplace_back( + cudaq::spin_operator::z(0).get_terms()[0].to_string(false)); + return opNames; + }(); + // This is a callback - if (!parameters.empty()) { + if (!parameters.empty() && + std::find(g_knownNonParametricOps.begin(), g_knownNonParametricOps.end(), + elemOp.to_string(false)) == g_knownNonParametricOps.end()) { const std::map> sortedParameters( parameters.begin(), parameters.end()); auto ks = std::views::keys(sortedParameters);